summaryrefslogtreecommitdiffstats
path: root/qmake
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2012-09-05 18:29:19 +0200
committerQt by Nokia <qt-info@nokia.com>2012-09-11 00:13:01 +0200
commit0e78e5080262b8fa7a86f7cd0c8716839db048f6 (patch)
tree83ca3f608a6c9a359ba783d496ffdbc9ef7a6073 /qmake
parented7594db5db7b28ce4c4646624de97b52fbf16d5 (diff)
port qmake to qt creator's qmake language evaluator
this is a monster commit which does the following things: - import the evaluator as-is from qt creator into qmake/library/ - integrate it into qmake's makefiles - overwrite proitems.h with actual special types - remove the parts of Option which are redundant with QMakeGlobals - make QMakeProperty a singleton owned by Option::globals. the dynamic handling so far made no sense. - make QMakeProject a subclass of QMakeEvaluator, with relatively few extensions the changes to existing qmake code outside project.* and option.* are minor. implementing the changes gradually would mean changing a lot of code which will be just replaced in the next commit, so i'm not wasting my time on it. Change-Id: I9746650423b8c5b3fbd8c3979a73228982a46195 Reviewed-by: Qt Doc Bot <qt_docbot@qt-project.org> Reviewed-by: Joerg Bornemann <joerg.bornemann@nokia.com>
Diffstat (limited to 'qmake')
-rw-r--r--qmake/Makefile.unix28
-rw-r--r--qmake/Makefile.win329
-rw-r--r--qmake/Makefile.win32-g++8
-rw-r--r--qmake/generators/makefile.cpp6
-rw-r--r--qmake/generators/metamakefile.cpp4
-rw-r--r--qmake/generators/projectgenerator.cpp20
-rw-r--r--qmake/generators/win32/msvc_vcproj.cpp6
-rw-r--r--qmake/library/ioutils.cpp168
-rw-r--r--qmake/library/ioutils.h83
-rw-r--r--qmake/library/proitems.cpp452
-rw-r--r--qmake/library/proitems.h264
-rw-r--r--qmake/library/qmakebuiltins.cpp1684
-rw-r--r--qmake/library/qmakeevaluator.cpp1981
-rw-r--r--qmake/library/qmakeevaluator.h309
-rw-r--r--qmake/library/qmakeevaluator_p.h107
-rw-r--r--qmake/library/qmakeglobals.cpp368
-rw-r--r--qmake/library/qmakeglobals.h173
-rw-r--r--qmake/library/qmakeparser.cpp1185
-rw-r--r--qmake/library/qmakeparser.h214
-rw-r--r--qmake/main.cpp11
-rw-r--r--qmake/meta.cpp4
-rw-r--r--qmake/option.cpp295
-rw-r--r--qmake/option.h35
-rw-r--r--qmake/project.cpp3758
-rw-r--r--qmake/project.h171
-rw-r--r--qmake/qmake.pri8
-rw-r--r--qmake/qmake.pro3
27 files changed, 7252 insertions, 4102 deletions
diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix
index c888c518e0..c8ac221293 100644
--- a/qmake/Makefile.unix
+++ b/qmake/Makefile.unix
@@ -11,8 +11,10 @@ QMKLIBSRC = $(QMKSRC)/library
QMKGENSRC = $(QMKSRC)/generators
#qmake code
-OBJS=project.o property.o main.o makefile.o unixmake2.o unixmake.o \
- mingw_make.o option.o winmakefile.o projectgenerator.o \
+OBJS=project.o option.o property.o main.o ioutils.o proitems.o \
+ qmakeglobals.o qmakeparser.o qmakeevaluator.o qmakebuiltins.o \
+ makefile.o unixmake2.o unixmake.o \
+ mingw_make.o winmakefile.o projectgenerator.o \
meta.o makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \
msvc_vcproj.o msvc_vcxproj.o msvc_nmake.o msvc_objectmodel.o msbuild_objectmodel.o \
gbuild.o cesdkhandler.o
@@ -36,6 +38,8 @@ QOBJS=qtextcodec.o qutfcodec.o qstring.o qstringbuilder.o qtextstream.o qiodevic
DEPEND_SRC = \
$(QMKSRC)/main.cpp $(QMKSRC)/project.cpp $(QMKSRC)/option.cpp $(QMKSRC)/property.cpp \
$(QMKSRC)/meta.cpp \
+ $(QMKLIBSRC)/ioutils.cpp $(QMKLIBSRC)/proitems.cpp $(QMKLIBSRC)/qmakeglobals.cpp \
+ $(QMKLIBSRC)/qmakeparser.cpp $(QMKLIBSRC)/qmakeevaluator.cpp $(QMKLIBSRC)/qmakebuiltins.cpp \
$(QMKGENSRC)/makefiledeps.cpp $(QMKGENSRC)/metamakefile.cpp \
$(QMKGENSRC)/projectgenerator.cpp $(QMKGENSRC)/makefile.cpp \
$(QMKGENSRC)/unix/unixmake.cpp $(QMKGENSRC)/unix/unixmake2.cpp \
@@ -90,7 +94,7 @@ CPPFLAGS = -g $(OPENSOURCE_CXXFLAGS) \
-I$(BUILD_PATH)/src/corelib/global -DHAVE_QCONFIG_CPP \
-I$(QMAKESPEC) \
-I$(SOURCE_PATH)/tools/shared \
- -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED \
+ -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DPROEVALUATOR_FULL -DPROEVALUATOR_DEBUG \
-DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_COMPRESS \
-DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM
@@ -112,6 +116,24 @@ depend:
makedepend -D__MAKEDEPEND__ $(CPPFLAGS) $(DEPEND_SRC)
+ioutils.o: $(QMKLIBSRC)/ioutils.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
+proitems.o: $(QMKLIBSRC)/proitems.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
+qmakeglobals.o: $(QMKLIBSRC)/qmakeglobals.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
+qmakeparser.o: $(QMKLIBSRC)/qmakeparser.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
+qmakeevaluator.o: $(QMKLIBSRC)/qmakeevaluator.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
+qmakebuiltins.o: $(QMKLIBSRC)/qmakebuiltins.cpp
+ $(CXX) -c -o $@ $(CXXFLAGS) $<
+
project.o: $(QMKSRC)/project.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<
diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32
index b97dec863e..f560cf8cb1 100644
--- a/qmake/Makefile.win32
+++ b/qmake/Makefile.win32
@@ -38,7 +38,7 @@ CFLAGS_BARE = -c -Fo./ \
-I$(BUILD_PATH)\src\corelib\global -DHAVE_QCONFIG_CPP \
-I$(SOURCE_PATH)\mkspecs\$(QMAKESPEC) \
-I$(SOURCE_PATH)\tools\shared \
- -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED \
+ -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DPROEVALUATOR_FULL -DPROEVALUATOR_DEBUG \
-DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_COMPRESS \
-DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \
-DUNICODE
@@ -57,7 +57,9 @@ ADDCLEAN = vc60.pdb vc70.pdb qmake.pdb qmake.ilk
!ENDIF
#qmake code
-OBJS = project.obj main.obj makefile.obj unixmake.obj unixmake2.obj mingw_make.obj \
+OBJS = project.obj main.obj ioutils.obj proitems.obj \
+ qmakeglobals.obj qmakeparser.obj qmakeevaluator.obj qmakebuiltins.obj \
+ makefile.obj unixmake.obj unixmake2.obj mingw_make.obj \
option.obj winmakefile.obj projectgenerator.obj property.obj meta.obj \
makefiledeps.obj metamakefile.obj xmloutput.obj pbuilder_pbx.obj \
msvc_nmake.obj msvc_vcproj.obj msvc_vcxproj.obj \
@@ -164,6 +166,9 @@ $(QTOBJS): qmake_pch.obj
qmake_pch.obj:
$(CXX) $(CXXFLAGS_BARE) -c -Yc -Fpqmake_pch.pch -TP $(QMKSRC)\qmake_pch.h
+{$(SOURCE_PATH)\qmake\library}.cpp{}.obj::
+ $(CXX) $(CXXFLAGS) $<
+
{$(SOURCE_PATH)\qmake\generators\mac}.cpp{}.obj::
$(CXX) $(CXXFLAGS) $<
diff --git a/qmake/Makefile.win32-g++ b/qmake/Makefile.win32-g++
index 8a29494f01..7bc24edef9 100644
--- a/qmake/Makefile.win32-g++
+++ b/qmake/Makefile.win32-g++
@@ -49,7 +49,7 @@ CFLAGS = -c -o$@ -O \
-I$(BUILD_PATH)/src/corelib/global -DHAVE_QCONFIG_CPP \
-I$(SOURCE_PATH)/mkspecs/win32-g++ \
-I$(SOURCE_PATH)/tools/shared \
- -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED \
+ -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DPROEVALUATOR_FULL -DPROEVALUATOR_DEBUG \
-DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_COMPRESS \
-DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \
-DUNICODE
@@ -61,7 +61,9 @@ ADDCLEAN =
#qmake code
-OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \
+OBJS = project.o main.o ioutils.o proitems.o \
+ qmakeglobals.o qmakeparser.o qmakeevaluator.o qmakebuiltins.o \
+ makefile.o unixmake.o unixmake2.o mingw_make.o \
option.o winmakefile.o projectgenerator.o property.o meta.o \
makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \
msvc_nmake.o msvc_vcproj.o msvc_vcxproj.o \
@@ -149,7 +151,7 @@ distclean:: clean
$(CXX) $(CXXFLAGS) $<
QTVPATH = $(TOOLSRC)/shared/windows:$(CORESRC)/global:$(CORESRC)/kernel:$(CORESRC)/tools:$(CORESRC)/codecs:$(CORESRC)/io:$(CORESRC)/xml:$(CORESRC)/plugin:$(BUILD_PATH)/src/corelib/global
-VPATH = $(QMKSRC):$(QMKSRC)/generators:$(QMKSRC)/generators/unix:$(QMKSRC)/generators/mac:$(QMKSRC)/generators/win32:$(QMKSRC)/generators/integrity:$(QTVPATH)
+VPATH = $(QMKSRC):$(QMKLIBSRC):$(QMKSRC)/generators:$(QMKSRC)/generators/unix:$(QMKSRC)/generators/mac:$(QMKSRC)/generators/win32:$(QMKSRC)/generators/integrity:$(QTVPATH)
project.o: $(QMKSRC)/project.h $(QMKSRC)/option.h
meta.o: $(QMKSRC)/project.h $(QMKSRC)/option.h
diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp
index 2f7e8c7bab..2cfbccb2b5 100644
--- a/qmake/generators/makefile.cpp
+++ b/qmake/generators/makefile.cpp
@@ -194,7 +194,7 @@ MakefileGenerator::initOutPaths()
ProValueMap &v = project->variables();
//for shadow builds
if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) {
- if (Option::mkfile::do_cache && !project->cacheFile().isEmpty() &&
+ if (Option::globals->do_cache && !project->cacheFile().isEmpty() &&
v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) {
QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first().toQString();
root = QDir::fromNativeSeparators(root);
@@ -884,7 +884,7 @@ MakefileGenerator::init()
// escape qmake command
project->values("QMAKE_QMAKE") =
- ProStringList(escapeFilePath(Option::fixPathToTargetOS(Option::qmake_abslocation, false)));
+ ProStringList(escapeFilePath(Option::fixPathToTargetOS(Option::globals->qmake_abslocation, false)));
}
bool
@@ -2682,7 +2682,7 @@ MakefileGenerator::writeMakeQmake(QTextStream &t, bool noDummyQmakeAll)
if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
t << escapeFilePath(ofile) << ": "
<< escapeDependencyPath(fileFixify(project->projectFile())) << " ";
- if (Option::mkfile::do_cache) {
+ if (Option::globals->do_cache) {
if (!project->confFile().isEmpty())
t << escapeDependencyPath(fileFixify(project->confFile())) << " ";
if (!project->cacheFile().isEmpty())
diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp
index c1e7efd62c..4c40ea06ee 100644
--- a/qmake/generators/metamakefile.cpp
+++ b/qmake/generators/metamakefile.cpp
@@ -235,7 +235,7 @@ MakefileGenerator
basevars["BUILD_NAME"] = (buildname.isEmpty() ? ProStringList(build) : buildname);
//create project
- QMakeProject *build_proj = new QMakeProject(project->properties());
+ QMakeProject *build_proj = new QMakeProject;
build_proj->setExtraVars(basevars);
build_proj->setExtraConfigs(basecfgs);
@@ -317,7 +317,7 @@ SubdirsMetaMakefileGenerator::init()
}
//handle sub project
- QMakeProject *sub_proj = new QMakeProject(project->properties());
+ QMakeProject *sub_proj = new QMakeProject;
for (int ind = 0; ind < sub->indent; ++ind)
printf(" ");
sub->input_dir = subdir.absolutePath();
diff --git a/qmake/generators/projectgenerator.cpp b/qmake/generators/projectgenerator.cpp
index aefa5ac6aa..0d3f6a019f 100644
--- a/qmake/generators/projectgenerator.cpp
+++ b/qmake/generators/projectgenerator.cpp
@@ -76,14 +76,17 @@ ProjectGenerator::init()
init_flag = true;
verifyCompilers();
- project->read(QMakeProject::ReadFeatures);
+ project->loadSpec();
+ project->evaluateFeatureFile("default_pre.prf");
+ project->evaluateFeatureFile("default_post.prf");
+ project->evaluateConfigFeatures();
project->values("CONFIG").clear();
Option::postProcessProject(project);
ProValueMap &v = project->variables();
- QString templ = Option::user_template.isEmpty() ? QString("app") : Option::user_template;
- if(!Option::user_template_prefix.isEmpty())
- templ.prepend(Option::user_template_prefix);
+ QString templ = Option::globals->user_template.isEmpty() ? QString("app") : Option::globals->user_template;
+ if (!Option::globals->user_template_prefix.isEmpty())
+ templ.prepend(Option::globals->user_template_prefix);
v["TEMPLATE_ASSIGN"] += templ;
//the scary stuff
@@ -344,9 +347,8 @@ ProjectGenerator::writeMakefile(QTextStream &t)
t << "######################################################################" << endl;
t << "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl;
t << "######################################################################" << endl << endl;
- int i;
- for(i = 0; i < Option::before_user_vars.size(); ++i)
- t << Option::before_user_vars[i] << endl;
+ if (!Option::globals->precmds.isEmpty())
+ t << Option::globals->precmds << endl;
t << getWritableVar("TEMPLATE_ASSIGN", false);
if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
t << endl << "# Directories" << "\n"
@@ -373,8 +375,8 @@ ProjectGenerator::writeMakefile(QTextStream &t)
<< getWritableVar("RESOURCES")
<< getWritableVar("TRANSLATIONS");
}
- for(i = 0; i < Option::after_user_vars.size(); ++i)
- t << Option::after_user_vars[i] << endl;
+ if (!Option::globals->postcmds.isEmpty())
+ t << Option::globals->postcmds << endl;
return true;
}
diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp
index b27b638d42..db88dcc152 100644
--- a/qmake/generators/win32/msvc_vcproj.cpp
+++ b/qmake/generators/win32/msvc_vcproj.cpp
@@ -442,8 +442,8 @@ void VcprojGenerator::writeSubDirs(QTextStream &t)
// Make sure that all temp projects are configured
// for release so that the depends are created
// without the debug <lib>dxxx.lib name mangling
- QStringList old_after_vars = Option::after_user_vars;
- Option::after_user_vars.append("CONFIG+=release");
+ QString old_after_vars = Option::globals->postcmds;
+ Option::globals->postcmds.append("\nCONFIG+=release");
QStringList subdirs = collectSubDirs(project);
for(int i = 0; i < subdirs.size(); ++i) {
@@ -627,7 +627,7 @@ nextfile:
t << _slnProjDepBeg;
// Restore previous after_user_var options
- Option::after_user_vars = old_after_vars;
+ Option::globals->postcmds = old_after_vars;
// Figure out dependencies
for(QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) {
diff --git a/qmake/library/ioutils.cpp b/qmake/library/ioutils.cpp
new file mode 100644
index 0000000000..c66952b345
--- /dev/null
+++ b/qmake/library/ioutils.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ioutils.h"
+
+#include <qdir.h>
+#include <qfile.h>
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QMakeInternal;
+
+IoUtils::FileType IoUtils::fileType(const QString &fileName)
+{
+ Q_ASSERT(fileName.isEmpty() || isAbsolutePath(fileName));
+#ifdef Q_OS_WIN
+ DWORD attr = GetFileAttributesW((WCHAR*)fileName.utf16());
+ if (attr == INVALID_FILE_ATTRIBUTES)
+ return FileNotFound;
+ return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FileIsDir : FileIsRegular;
+#else
+ struct ::stat st;
+ if (::stat(fileName.toLocal8Bit().constData(), &st))
+ return FileNotFound;
+ return S_ISDIR(st.st_mode) ? FileIsDir : FileIsRegular;
+#endif
+}
+
+bool IoUtils::isRelativePath(const QString &path)
+{
+ if (path.startsWith(QLatin1Char('/')))
+ return false;
+#ifdef Q_OS_WIN
+ if (path.startsWith(QLatin1Char('\\')))
+ return false;
+ // Unlike QFileInfo, this won't accept a relative path with a drive letter.
+ // Such paths result in a royal mess anyway ...
+ if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter()
+ && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\')))
+ return false;
+#endif
+ return true;
+}
+
+QStringRef IoUtils::fileName(const QString &fileName)
+{
+ return fileName.midRef(fileName.lastIndexOf(QLatin1Char('/')) + 1);
+}
+
+QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
+{
+ if (fileName.isEmpty())
+ return QString();
+ if (isAbsolutePath(fileName))
+ return QDir::cleanPath(fileName);
+ return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
+}
+
+inline static
+bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
+{
+ for (int x = arg.length() - 1; x >= 0; --x) {
+ ushort c = arg.unicode()[x].unicode();
+ if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
+ return true;
+ }
+ return false;
+}
+
+QString IoUtils::shellQuoteUnix(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
+ 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
+ }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+
+ if (!arg.length())
+ return QString::fromLatin1("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
+ ret.prepend(QLatin1Char('\''));
+ ret.append(QLatin1Char('\''));
+ }
+ return ret;
+}
+
+QString IoUtils::shellQuoteWin(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ // - control chars & space
+ // - the shell meta chars "&()<>^|
+ // - the potential separators ,;=
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
+ };
+
+ if (!arg.length())
+ return QString::fromLatin1("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ // Quotes are escaped and their preceding backslashes are doubled.
+ // It's impossible to escape anything inside a quoted string on cmd
+ // level, so the outer quoting must be "suspended".
+ ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ int i = ret.length();
+ while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
+ --i;
+ ret.insert(i, QLatin1Char('"'));
+ ret.prepend(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/library/ioutils.h b/qmake/library/ioutils.h
new file mode 100644
index 0000000000..35410e3db5
--- /dev/null
+++ b/qmake/library/ioutils.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IOUTILS_H
+#define IOUTILS_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QMakeInternal {
+
+/*!
+ This class provides replacement functionality for QFileInfo, QFile & QDir,
+ as these are abysmally slow.
+*/
+class IoUtils {
+public:
+ enum FileType {
+ FileNotFound = 0,
+ FileIsRegular = 1,
+ FileIsDir = 2
+ };
+
+ static FileType fileType(const QString &fileName);
+ static bool exists(const QString &fileName) { return fileType(fileName) != FileNotFound; }
+ static bool isRelativePath(const QString &fileName);
+ static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
+ static QStringRef fileName(const QString &fileName); // Requires normalized path
+ static QString resolvePath(const QString &baseDir, const QString &fileName);
+ static QString shellQuoteUnix(const QString &arg);
+ static QString shellQuoteWin(const QString &arg);
+ static QString shellQuote(const QString &arg)
+#ifdef Q_OS_UNIX
+ { return shellQuoteUnix(arg); }
+#else
+ { return shellQuoteWin(arg); }
+#endif
+};
+
+} // namespace ProFileEvaluatorInternal
+
+QT_END_NAMESPACE
+
+#endif // IOUTILS_H
diff --git a/qmake/library/proitems.cpp b/qmake/library/proitems.cpp
new file mode 100644
index 0000000000..ba02c727b1
--- /dev/null
+++ b/qmake/library/proitems.cpp
@@ -0,0 +1,452 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "proitems.h"
+
+#include <qfileinfo.h>
+#include <qset.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+QT_BEGIN_NAMESPACE
+
+// from qhash.cpp
+uint ProString::hash(const QChar *p, int n)
+{
+ uint h = 0;
+
+ while (n--) {
+ h = (h << 4) + (*p++).unicode();
+ h ^= (h & 0xf0000000) >> 23;
+ h &= 0x0fffffff;
+ }
+ return h;
+}
+
+ProString::ProString() :
+ m_offset(0), m_length(0), m_file(0), m_hash(0x80000000)
+{
+}
+
+ProString::ProString(const ProString &other) :
+ m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash)
+{
+}
+
+ProString::ProString(const ProString &other, OmitPreHashing) :
+ m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000)
+{
+}
+
+ProString::ProString(const QString &str, DoPreHashing) :
+ m_string(str), m_offset(0), m_length(str.length()), m_file(0)
+{
+ updatedHash();
+}
+
+ProString::ProString(const QString &str) :
+ m_string(str), m_offset(0), m_length(str.length()), m_file(0), m_hash(0x80000000)
+{
+}
+
+ProString::ProString(const char *str, DoPreHashing) :
+ m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0)
+{
+ updatedHash();
+}
+
+ProString::ProString(const char *str) :
+ m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000)
+{
+}
+
+ProString::ProString(const QString &str, int offset, int length, DoPreHashing) :
+ m_string(str), m_offset(offset), m_length(length), m_file(0)
+{
+ updatedHash();
+}
+
+ProString::ProString(const QString &str, int offset, int length, uint hash) :
+ m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash)
+{
+}
+
+ProString::ProString(const QString &str, int offset, int length) :
+ m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000)
+{
+}
+
+void ProString::setValue(const QString &str)
+{
+ m_string = str, m_offset = 0, m_length = str.length(), m_hash = 0x80000000;
+}
+
+uint ProString::updatedHash() const
+{
+ return (m_hash = hash(m_string.constData() + m_offset, m_length));
+}
+
+uint qHash(const ProString &str)
+{
+ if (!(str.m_hash & 0x80000000))
+ return str.m_hash;
+ return str.updatedHash();
+}
+
+ProKey::ProKey(const QString &str) :
+ ProString(str, DoHash)
+{
+}
+
+ProKey::ProKey(const char *str) :
+ ProString(str, DoHash)
+{
+}
+
+ProKey::ProKey(const QString &str, int off, int len) :
+ ProString(str, off, len, DoHash)
+{
+}
+
+ProKey::ProKey(const QString &str, int off, int len, uint hash) :
+ ProString(str, off, len, hash)
+{
+}
+
+void ProKey::setValue(const QString &str)
+{
+ m_string = str, m_offset = 0, m_length = str.length();
+ updatedHash();
+}
+
+QString ProString::toQString() const
+{
+ return m_string.mid(m_offset, m_length);
+}
+
+QString &ProString::toQString(QString &tmp) const
+{
+ return tmp.setRawData(m_string.constData() + m_offset, m_length);
+}
+
+QChar *ProString::prepareExtend(int extraLen, int thisTarget, int extraTarget)
+{
+ if (m_string.isDetached() && m_length + extraLen <= m_string.capacity()) {
+ m_string.reserve(0); // Prevent the resize() below from reallocating
+ QChar *ptr = (QChar *)m_string.constData();
+ if (m_offset != thisTarget)
+ memmove(ptr + thisTarget, ptr + m_offset, m_length * 2);
+ ptr += extraTarget;
+ m_offset = 0;
+ m_length += extraLen;
+ m_string.resize(m_length);
+ m_hash = 0x80000000;
+ return ptr;
+ } else {
+ QString neu(m_length + extraLen, Qt::Uninitialized);
+ QChar *ptr = (QChar *)neu.constData();
+ memcpy(ptr + thisTarget, m_string.constData() + m_offset, m_length * 2);
+ ptr += extraTarget;
+ *this = ProString(neu);
+ return ptr;
+ }
+}
+
+ProString &ProString::prepend(const ProString &other)
+{
+ if (other.m_length) {
+ if (!m_length) {
+ *this = other;
+ } else {
+ QChar *ptr = prepareExtend(other.m_length, other.m_length, 0);
+ memcpy(ptr, other.constData(), other.m_length * 2);
+ if (!m_file)
+ m_file = other.m_file;
+ }
+ }
+ return *this;
+}
+
+ProString &ProString::append(const QLatin1String other)
+{
+ const char *latin1 = other.latin1();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ int size = other.size();
+#else
+ int size = strlen(latin1);
+#endif
+ if (size) {
+ QChar *ptr = prepareExtend(size, 0, m_length);
+ for (int i = 0; i < size; i++)
+ *ptr++ = QLatin1Char(latin1[i]);
+ }
+ return *this;
+}
+
+ProString &ProString::append(QChar other)
+{
+ QChar *ptr = prepareExtend(1, 0, m_length);
+ *ptr = other;
+ return *this;
+}
+
+// If pending != 0, prefix with space if appending to non-empty non-pending
+ProString &ProString::append(const ProString &other, bool *pending)
+{
+ if (other.m_length) {
+ if (!m_length) {
+ *this = other;
+ } else {
+ QChar *ptr;
+ if (pending && !*pending) {
+ ptr = prepareExtend(1 + other.m_length, 0, m_length);
+ *ptr++ = 32;
+ } else {
+ ptr = prepareExtend(other.m_length, 0, m_length);
+ }
+ memcpy(ptr, other.m_string.constData() + other.m_offset, other.m_length * 2);
+ if (other.m_file)
+ m_file = other.m_file;
+ }
+ if (pending)
+ *pending = true;
+ }
+ return *this;
+}
+
+ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st)
+{
+ if (const int sz = other.size()) {
+ int startIdx = 0;
+ if (pending && !*pending && skipEmpty1st && other.at(0).isEmpty()) {
+ if (sz == 1)
+ return *this;
+ startIdx = 1;
+ }
+ if (!m_length && sz == startIdx + 1) {
+ *this = other.at(startIdx);
+ } else {
+ int totalLength = sz - startIdx;
+ for (int i = startIdx; i < sz; ++i)
+ totalLength += other.at(i).size();
+ bool putSpace = false;
+ if (pending && !*pending && m_length)
+ putSpace = true;
+ else
+ totalLength--;
+
+ QChar *ptr = prepareExtend(totalLength, 0, m_length);
+ for (int i = startIdx; i < sz; ++i) {
+ if (putSpace)
+ *ptr++ = 32;
+ else
+ putSpace = true;
+ const ProString &str = other.at(i);
+ memcpy(ptr, str.m_string.constData() + str.m_offset, str.m_length * 2);
+ ptr += str.m_length;
+ }
+ if (other.last().m_file)
+ m_file = other.last().m_file;
+ }
+ if (pending)
+ *pending = true;
+ }
+ return *this;
+}
+
+QString operator+(const ProString &one, const ProString &two)
+{
+ if (two.m_length) {
+ if (!one.m_length) {
+ return two.toQString();
+ } else {
+ QString neu(one.m_length + two.m_length, Qt::Uninitialized);
+ ushort *ptr = (ushort *)neu.constData();
+ memcpy(ptr, one.m_string.constData() + one.m_offset, one.m_length * 2);
+ memcpy(ptr + one.m_length, two.m_string.constData() + two.m_offset, two.m_length * 2);
+ return neu;
+ }
+ }
+ return one.toQString();
+}
+
+
+ProString ProString::mid(int off, int len) const
+{
+ ProString ret(*this, NoHash);
+ if (off > m_length)
+ off = m_length;
+ ret.m_offset += off;
+ ret.m_length -= off;
+ if ((uint)ret.m_length > (uint)len) // Unsigned comparison to interpret < 0 as infinite
+ ret.m_length = len;
+ return ret;
+}
+
+ProString ProString::trimmed() const
+{
+ ProString ret(*this, NoHash);
+ int cur = m_offset;
+ int end = cur + m_length;
+ const QChar *data = m_string.constData();
+ for (; cur < end; cur++)
+ if (!data[cur].isSpace()) {
+ // No underrun check - we know there is at least one non-whitespace
+ while (data[end - 1].isSpace())
+ end--;
+ break;
+ }
+ ret.m_offset = cur;
+ ret.m_length = end - cur;
+ return ret;
+}
+
+QTextStream &operator<<(QTextStream &t, const ProString &str)
+{
+ t << str.toQString(); // XXX optimize ... somehow
+ return t;
+}
+
+QString ProStringList::join(const QString &sep) const
+{
+ int totalLength = 0;
+ const int sz = size();
+
+ for (int i = 0; i < sz; ++i)
+ totalLength += at(i).size();
+
+ if (sz)
+ totalLength += sep.size() * (sz - 1);
+
+ QString res(totalLength, Qt::Uninitialized);
+ QChar *ptr = (QChar *)res.constData();
+ for (int i = 0; i < sz; ++i) {
+ if (i) {
+ memcpy(ptr, sep.constData(), sep.size() * 2);
+ ptr += sep.size();
+ }
+ memcpy(ptr, at(i).constData(), at(i).size() * 2);
+ ptr += at(i).size();
+ }
+ return res;
+}
+
+void ProStringList::removeAll(const ProString &str)
+{
+ for (int i = size(); --i >= 0; )
+ if (at(i) == str)
+ remove(i);
+}
+
+void ProStringList::removeAll(const char *str)
+{
+ for (int i = size(); --i >= 0; )
+ if (at(i) == str)
+ remove(i);
+}
+
+void ProStringList::removeDuplicates()
+{
+ int n = size();
+ int j = 0;
+ QSet<ProString> seen;
+ seen.reserve(n);
+ for (int i = 0; i < n; ++i) {
+ const ProString &s = at(i);
+ if (seen.contains(s))
+ continue;
+ seen.insert(s);
+ if (j != i)
+ (*this)[j] = s;
+ ++j;
+ }
+ if (n != j)
+ erase(begin() + j, end());
+}
+
+ProStringList::ProStringList(const QStringList &list)
+{
+ reserve(list.size());
+ foreach (const QString &str, list)
+ *this << ProString(str);
+}
+
+QStringList ProStringList::toQStringList() const
+{
+ QStringList ret;
+ ret.reserve(size());
+ foreach (const ProString &str, *this)
+ ret << str.toQString();
+ return ret;
+}
+
+bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const
+{
+ for (int i = 0; i < size(); i++)
+ if (!at(i).compare(str, cs))
+ return true;
+ return false;
+}
+
+bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const
+{
+ for (int i = 0; i < size(); i++)
+ if (!at(i).compare(str, cs))
+ return true;
+ return false;
+}
+
+ProFile::ProFile(const QString &fileName)
+ : m_refCount(1),
+ m_fileName(fileName),
+ m_ok(true),
+ m_hostBuild(false)
+{
+ if (!fileName.startsWith(QLatin1Char('(')))
+ m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory!
+ fileName.left(fileName.lastIndexOf(QLatin1Char('/')))).canonicalFilePath();
+}
+
+ProFile::~ProFile()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h
index 994bfcc75d..b712ca371b 100644
--- a/qmake/library/proitems.h
+++ b/qmake/library/proitems.h
@@ -44,14 +44,29 @@
#include "qmake_global.h"
-#include <QString>
-#include <QStringList>
-#include <QHash>
-#include <QTextStream>
+#include <qstring.h>
+#include <qvector.h>
+#include <qhash.h>
QT_BEGIN_NAMESPACE
-#if 0
+class QTextStream;
+
+#ifdef PROPARSER_THREAD_SAFE
+typedef QAtomicInt ProItemRefCount;
+#else
+class ProItemRefCount {
+public:
+ ProItemRefCount(int cnt = 0) : m_cnt(cnt) {}
+ bool ref() { return ++m_cnt != 0; }
+ bool deref() { return --m_cnt != 0; }
+ ProItemRefCount &operator=(int value) { m_cnt = value; return *this; }
+private:
+ int m_cnt;
+};
+#endif
+
+#ifndef QT_BUILD_QMAKE
# define PROITEM_EXPLICIT explicit
#else
# define PROITEM_EXPLICIT
@@ -59,27 +74,36 @@ QT_BEGIN_NAMESPACE
class ProKey;
class ProStringList;
+class ProFile;
class ProString {
public:
- ProString() {}
- ProString(const ProString &other) : m_string(other.m_string) {}
- PROITEM_EXPLICIT ProString(const QString &str) : m_string(str) {}
- PROITEM_EXPLICIT ProString(const char *str) : m_string(QLatin1String(str)) {}
- void clear() { m_string.clear(); }
-
- ProString &prepend(const ProString &other) { m_string.prepend(other.m_string); return *this; }
- ProString &append(const ProString &other) { m_string.append(other.m_string); return *this; }
- ProString &append(const QString &other) { m_string.append(other); return *this; }
- ProString &append(const char *other) { m_string.append(QLatin1String(other)); return *this; }
- ProString &append(QChar other) { m_string.append(other); return *this; }
+ ProString();
+ ProString(const ProString &other);
+ PROITEM_EXPLICIT ProString(const QString &str);
+ PROITEM_EXPLICIT ProString(const char *str);
+ ProString(const QString &str, int offset, int length);
+ void setValue(const QString &str);
+ void clear() { m_string.clear(); m_length = 0; }
+ ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; }
+ ProString &setSource(const ProFile *pro) { m_file = pro; return *this; }
+ const ProFile *sourceFile() const { return m_file; }
+
+ ProString &prepend(const ProString &other);
+ ProString &append(const ProString &other, bool *pending = 0);
+ ProString &append(const QString &other) { return append(ProString(other)); }
+ ProString &append(const QLatin1String other);
+ ProString &append(const char *other) { return append(QLatin1String(other)); }
+ ProString &append(QChar other);
+ ProString &append(const ProStringList &other, bool *pending = 0, bool skipEmpty1st = false);
ProString &operator+=(const ProString &other) { return append(other); }
ProString &operator+=(const QString &other) { return append(other); }
+ ProString &operator+=(const QLatin1String other) { return append(other); }
ProString &operator+=(const char *other) { return append(other); }
ProString &operator+=(QChar other) { return append(other); }
- void chop(int n) { m_string.chop(n); }
- void chopFront(int n) { m_string.remove(0, n); }
+ void chop(int n) { Q_ASSERT(n <= m_length); m_length -= n; }
+ void chopFront(int n) { Q_ASSERT(n <= m_length); m_offset += n; m_length -= n; }
bool operator==(const ProString &other) const { return toQStringRef() == other.toQStringRef(); }
bool operator==(const QString &other) const { return toQStringRef() == other; }
@@ -90,15 +114,15 @@ public:
bool operator!=(QLatin1String other) const { return !(*this == other); }
bool operator!=(const char *other) const { return !(*this == other); }
bool isNull() const { return m_string.isNull(); }
- bool isEmpty() const { return m_string.isEmpty(); }
- int length() const { return m_string.size(); }
- int size() const { return m_string.size(); }
- QChar at(int i) const { return m_string.at(i); }
- const QChar *constData() const { return m_string.constData(); }
- ProString mid(int off, int len = -1) const { return m_string.mid(off, len); }
+ bool isEmpty() const { return !m_length; }
+ int length() const { return m_length; }
+ int size() const { return m_length; }
+ QChar at(int i) const { Q_ASSERT((uint)i < (uint)m_length); return constData()[i]; }
+ const QChar *constData() const { return m_string.constData() + m_offset; }
+ ProString mid(int off, int len = -1) const;
ProString left(int len) const { return mid(0, len); }
ProString right(int len) const { return mid(qMax(0, size() - len)); }
- ProString trimmed() const { return m_string.trimmed(); }
+ ProString trimmed() const;
int compare(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub.toQStringRef(), cs); }
int compare(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub, cs); }
int compare(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(QLatin1String(sub), cs); }
@@ -122,12 +146,15 @@ public:
int toInt(bool *ok = 0) const { return toQString().toInt(ok); } // XXX optimize
short toShort(bool *ok = 0) const { return toQString().toShort(ok); } // XXX optimize
- ALWAYS_INLINE QStringRef toQStringRef() const { return QStringRef(&m_string, 0, m_string.length()); }
+ static uint hash(const QChar *p, int n);
+
+ ALWAYS_INLINE QStringRef toQStringRef() const { return QStringRef(&m_string, m_offset, m_length); }
ALWAYS_INLINE ProKey &toKey() { return *(ProKey *)this; }
ALWAYS_INLINE const ProKey &toKey() const { return *(const ProKey *)this; }
- QString toQString() const { return m_string; }
+ QString toQString() const;
+ QString &toQString(QString &tmp) const;
QByteArray toLatin1() const { return toQStringRef().toLatin1(); }
@@ -135,10 +162,23 @@ private:
ProString(const ProKey &other);
ProString &operator=(const ProKey &other);
+ enum OmitPreHashing { NoHash };
+ ProString(const ProString &other, OmitPreHashing);
+
+ enum DoPreHashing { DoHash };
+ ALWAYS_INLINE ProString(const QString &str, DoPreHashing);
+ ALWAYS_INLINE ProString(const char *str, DoPreHashing);
+ ALWAYS_INLINE ProString(const QString &str, int offset, int length, DoPreHashing);
+ ALWAYS_INLINE ProString(const QString &str, int offset, int length, uint hash);
+
QString m_string;
- friend uint qHash(const ProKey &str, uint seed);
+ int m_offset, m_length;
+ const ProFile *m_file;
+ mutable uint m_hash;
+ QChar *prepareExtend(int extraLen, int thisTarget, int extraTarget);
+ uint updatedHash() const;
+ friend uint qHash(const ProString &str);
friend QString operator+(const ProString &one, const ProString &two);
- friend QString &operator+=(QString &that, const ProString &other);
friend class ProKey;
};
Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
@@ -146,8 +186,11 @@ Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
class ProKey : public ProString {
public:
ALWAYS_INLINE ProKey() : ProString() {}
- explicit ProKey(const QString &str) : ProString(str) {}
- PROITEM_EXPLICIT ProKey(const char *str) : ProString(str) {}
+ explicit ProKey(const QString &str);
+ PROITEM_EXPLICIT ProKey(const char *str);
+ ProKey(const QString &str, int off, int len);
+ ProKey(const QString &str, int off, int len, uint hash);
+ void setValue(const QString &str);
#ifdef Q_CC_MSVC
// Workaround strange MSVC behaviour when exporting classes with ProKey members.
@@ -167,10 +210,8 @@ private:
};
Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE);
-inline uint qHash(const ProKey &key, uint seed = 0)
- { return qHash(key.m_string, seed); }
-inline QString operator+(const ProString &one, const ProString &two)
- { return one.m_string + two.m_string; }
+uint qHash(const ProString &str);
+QString operator+(const ProString &one, const ProString &two);
inline QString operator+(const ProString &one, const QString &two)
{ return one + ProString(two); }
inline QString operator+(const QString &one, const ProString &two)
@@ -189,29 +230,31 @@ inline bool operator==(const QString &that, const ProString &other)
inline bool operator!=(const QString &that, const ProString &other)
{ return !(other == that); }
-inline QTextStream &operator<<(QTextStream &t, const ProString &str)
- { t << str.toQString(); return t; }
+QTextStream &operator<<(QTextStream &t, const ProString &str);
-class ProStringList : public QList<ProString> {
+class ProStringList : public QVector<ProString> {
public:
ProStringList() {}
ProStringList(const ProString &str) { *this << str; }
- explicit ProStringList(const QStringList &list) : QList<ProString>(*(const ProStringList *)&list) {}
- QStringList toQStringList() const { return *(const QStringList *)this; }
+ explicit ProStringList(const QStringList &list);
+ QStringList toQStringList() const;
ProStringList &operator<<(const ProString &str)
- { QList<ProString>::operator<<(str); return *this; }
+ { QVector<ProString>::operator<<(str); return *this; }
- QString join(const QString &sep) const { return toQStringList().join(sep); }
+ int length() const { return size(); }
- void remove(int idx) { removeAt(idx); }
+ QString join(const QString &sep) const;
- bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
- { return contains(str.toQString(), cs); }
+ void removeAll(const ProString &str);
+ void removeAll(const char *str);
+ void removeAt(int idx) { remove(idx); }
+ void removeDuplicates();
+
+ bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
- { return (*(const QStringList *)this).contains(str, cs); }
- bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
- { return (*(const QStringList *)this).contains(str, cs); }
+ { return contains(ProString(str), cs); }
+ bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
};
Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE);
@@ -220,6 +263,131 @@ inline ProStringList operator+(const ProStringList &one, const ProStringList &tw
typedef QHash<ProKey, ProStringList> ProValueMap;
+// These token definitions affect both ProFileEvaluator and ProWriter
+enum ProToken {
+ TokTerminator = 0, // end of stream (possibly not included in length; must be zero)
+ TokLine, // line marker:
+ // - line (1)
+ TokAssign, // variable =
+ TokAppend, // variable +=
+ TokAppendUnique, // variable *=
+ TokRemove, // variable -=
+ TokReplace, // variable ~=
+ // previous literal/expansion is a variable manipulation
+ // - value expression + TokValueTerminator
+ TokValueTerminator, // assignment value terminator
+ TokLiteral, // literal string (fully dequoted)
+ // - length (1)
+ // - string data (length; unterminated)
+ TokHashLiteral, // literal string with hash (fully dequoted)
+ // - hash (2)
+ // - length (1)
+ // - string data (length; unterminated)
+ TokVariable, // qmake variable expansion
+ // - hash (2)
+ // - name length (1)
+ // - name (name length; unterminated)
+ TokProperty, // qmake property expansion
+ // - hash (2)
+ // - name length (1)
+ // - name (name length; unterminated)
+ TokEnvVar, // environment variable expansion
+ // - name length (1)
+ // - name (name length; unterminated)
+ TokFuncName, // replace function expansion
+ // - hash (2)
+ // - name length (1)
+ // - name (name length; unterminated)
+ // - ((nested expansion + TokArgSeparator)* + nested expansion)?
+ // - TokFuncTerminator
+ TokArgSeparator, // function argument separator
+ TokFuncTerminator, // function argument list terminator
+ TokCondition, // previous literal/expansion is a conditional
+ TokTestCall, // previous literal/expansion is a test function call
+ // - ((nested expansion + TokArgSeparator)* + nested expansion)?
+ // - TokFuncTerminator
+ TokNot, // '!' operator
+ TokAnd, // ':' operator
+ TokOr, // '|' operator
+ TokBranch, // branch point:
+ // - then block length (2)
+ // - then block + TokTerminator (then block length)
+ // - else block length (2)
+ // - else block + TokTerminator (else block length)
+ TokForLoop, // for loop:
+ // - variable name: hash (2), length (1), chars (length)
+ // - expression: length (2), bytes + TokValueTerminator (length)
+ // - body length (2)
+ // - body + TokTerminator (body length)
+ TokTestDef, // test function definition:
+ TokReplaceDef, // replace function definition:
+ // - function name: hash (2), length (1), chars (length)
+ // - body length (2)
+ // - body + TokTerminator (body length)
+ TokMask = 0xff,
+ TokQuoted = 0x100, // The expression is quoted => join expanded stringlist
+ TokNewStr = 0x200 // Next stringlist element
+};
+
+class QMAKE_EXPORT ProFile
+{
+public:
+ explicit ProFile(const QString &fileName);
+ ~ProFile();
+
+ QString fileName() const { return m_fileName; }
+ QString directoryName() const { return m_directoryName; }
+ const QString &items() const { return m_proitems; }
+ QString *itemsRef() { return &m_proitems; }
+ const ushort *tokPtr() const { return (const ushort *)m_proitems.constData(); }
+
+ void ref() { m_refCount.ref(); }
+ void deref() { if (!m_refCount.deref()) delete this; }
+
+ bool isOk() const { return m_ok; }
+ void setOk(bool ok) { m_ok = ok; }
+
+ bool isHostBuild() const { return m_hostBuild; }
+ void setHostBuild(bool host_build) { m_hostBuild = host_build; }
+
+private:
+ ProItemRefCount m_refCount;
+ QString m_proitems;
+ QString m_fileName;
+ QString m_directoryName;
+ bool m_ok;
+ bool m_hostBuild;
+};
+
+class ProFunctionDef {
+public:
+ ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
+ ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
+ ~ProFunctionDef() { m_pro->deref(); }
+ ProFunctionDef &operator=(const ProFunctionDef &o)
+ {
+ if (this != &o) {
+ m_pro->deref();
+ m_pro = o.m_pro;
+ m_pro->ref();
+ m_offset = o.m_offset;
+ }
+ return *this;
+ }
+ ProFile *pro() const { return m_pro; }
+ const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
+private:
+ ProFile *m_pro;
+ int m_offset;
+};
+
+Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE);
+
+struct ProFunctionDefs {
+ QHash<ProKey, ProFunctionDef> testFunctions;
+ QHash<ProKey, ProFunctionDef> replaceFunctions;
+};
+
QT_END_NAMESPACE
#endif // PROITEMS_H
diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp
new file mode 100644
index 0000000000..bf1c00f262
--- /dev/null
+++ b/qmake/library/qmakebuiltins.cpp
@@ -0,0 +1,1684 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeevaluator.h"
+
+#include "qmakeevaluator_p.h"
+#include "qmakeglobals.h"
+#include "qmakeparser.h"
+#include "ioutils.h"
+
+#include <qbytearray.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qregexp.h>
+#include <qset.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+#ifdef Q_OS_UNIX
+#include <time.h>
+#include <utime.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+using namespace QMakeInternal;
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+enum ExpandFunc {
+ E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
+ E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+ E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND,
+ E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE,
+ E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
+ E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
+ E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE
+};
+
+enum TestFunc {
+ T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
+ T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
+ T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
+ T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
+ T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
+};
+
+void QMakeEvaluator::initFunctionStatics()
+{
+ static const struct {
+ const char * const name;
+ const ExpandFunc func;
+ } expandInits[] = {
+ { "member", E_MEMBER },
+ { "first", E_FIRST },
+ { "last", E_LAST },
+ { "size", E_SIZE },
+ { "cat", E_CAT },
+ { "fromfile", E_FROMFILE },
+ { "eval", E_EVAL },
+ { "list", E_LIST },
+ { "sprintf", E_SPRINTF },
+ { "format_number", E_FORMAT_NUMBER },
+ { "join", E_JOIN },
+ { "split", E_SPLIT },
+ { "basename", E_BASENAME },
+ { "dirname", E_DIRNAME },
+ { "section", E_SECTION },
+ { "find", E_FIND },
+ { "system", E_SYSTEM },
+ { "unique", E_UNIQUE },
+ { "reverse", E_REVERSE },
+ { "quote", E_QUOTE },
+ { "escape_expand", E_ESCAPE_EXPAND },
+ { "upper", E_UPPER },
+ { "lower", E_LOWER },
+ { "re_escape", E_RE_ESCAPE },
+ { "val_escape", E_VAL_ESCAPE },
+ { "files", E_FILES },
+ { "prompt", E_PROMPT },
+ { "replace", E_REPLACE },
+ { "sort_depends", E_SORT_DEPENDS },
+ { "resolve_depends", E_RESOLVE_DEPENDS },
+ { "enumerate_vars", E_ENUMERATE_VARS },
+ { "shadowed", E_SHADOWED },
+ { "absolute_path", E_ABSOLUTE_PATH },
+ { "relative_path", E_RELATIVE_PATH },
+ { "clean_path", E_CLEAN_PATH },
+ { "system_path", E_SYSTEM_PATH },
+ { "shell_path", E_SHELL_PATH },
+ { "system_quote", E_SYSTEM_QUOTE },
+ { "shell_quote", E_SHELL_QUOTE },
+ };
+ for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
+ statics.expands.insert(ProKey(expandInits[i].name), expandInits[i].func);
+
+ static const struct {
+ const char * const name;
+ const TestFunc func;
+ } testInits[] = {
+ { "requires", T_REQUIRES },
+ { "greaterThan", T_GREATERTHAN },
+ { "lessThan", T_LESSTHAN },
+ { "equals", T_EQUALS },
+ { "isEqual", T_EQUALS },
+ { "exists", T_EXISTS },
+ { "export", T_EXPORT },
+ { "clear", T_CLEAR },
+ { "unset", T_UNSET },
+ { "eval", T_EVAL },
+ { "CONFIG", T_CONFIG },
+ { "if", T_IF },
+ { "isActiveConfig", T_CONFIG },
+ { "system", T_SYSTEM },
+ { "return", T_RETURN },
+ { "break", T_BREAK },
+ { "next", T_NEXT },
+ { "defined", T_DEFINED },
+ { "contains", T_CONTAINS },
+ { "infile", T_INFILE },
+ { "count", T_COUNT },
+ { "isEmpty", T_ISEMPTY },
+ { "load", T_LOAD },
+ { "include", T_INCLUDE },
+ { "debug", T_DEBUG },
+ { "log", T_LOG },
+ { "message", T_MESSAGE },
+ { "warning", T_WARNING },
+ { "error", T_ERROR },
+ { "mkpath", T_MKPATH },
+ { "write_file", T_WRITE_FILE },
+ { "touch", T_TOUCH },
+ { "cache", T_CACHE },
+ };
+ for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
+ statics.functions.insert(ProKey(testInits[i].name), testInits[i].func);
+}
+
+static bool isTrue(const ProString &_str, QString &tmp)
+{
+ const QString &str = _str.toQString(tmp);
+ return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
+}
+
+#ifdef Q_OS_WIN
+static QString windowsErrorCode()
+{
+ wchar_t *string = 0;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&string,
+ 0,
+ NULL);
+ QString ret = QString::fromWCharArray(string);
+ LocalFree((HLOCAL)string);
+ return ret;
+}
+#endif
+
+static QString
+quoteValue(const ProString &val)
+{
+ QString ret;
+ ret.reserve(val.size());
+ const QChar *chars = val.constData();
+ bool quote = val.isEmpty();
+ bool escaping = false;
+ for (int i = 0, l = val.size(); i < l; i++) {
+ QChar c = chars[i];
+ ushort uc = c.unicode();
+ if (uc < 32) {
+ if (!escaping) {
+ escaping = true;
+ ret += QLatin1String("$$escape_expand(");
+ }
+ switch (uc) {
+ case '\r':
+ ret += QLatin1String("\\\\r");
+ break;
+ case '\n':
+ ret += QLatin1String("\\\\n");
+ break;
+ case '\t':
+ ret += QLatin1String("\\\\t");
+ break;
+ default:
+ ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
+ break;
+ }
+ } else {
+ if (escaping) {
+ escaping = false;
+ ret += QLatin1Char(')');
+ }
+ switch (uc) {
+ case '\\':
+ ret += QLatin1String("\\\\");
+ break;
+ case '"':
+ ret += QLatin1String("\\\"");
+ break;
+ case '\'':
+ ret += QLatin1String("\\'");
+ break;
+ case '$':
+ ret += QLatin1String("\\$");
+ break;
+ case '#':
+ ret += QLatin1String("$${LITERAL_HASH}");
+ break;
+ case 32:
+ quote = true;
+ // fallthrough
+ default:
+ ret += c;
+ break;
+ }
+ }
+ }
+ if (escaping)
+ ret += QLatin1Char(')');
+ if (quote) {
+ ret.prepend(QLatin1Char('"'));
+ ret.append(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+static bool
+doWriteFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr)
+{
+ QByteArray bytes = contents.toLocal8Bit();
+ QFile cfile(name);
+ if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ if (cfile.readAll() == bytes)
+ return true;
+ cfile.close();
+ }
+ if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ cfile.write(bytes);
+ cfile.close();
+ if (cfile.error() != QFile::NoError) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ return true;
+}
+
+QMakeEvaluator::VisitReturn
+QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
+ const QString &contents)
+{
+ QFileInfo qfi(fn);
+ if (!QDir::current().mkpath(qfi.path())) {
+ evalError(fL1S("Cannot create %1directory %2.")
+ .arg(ctx, QDir::toNativeSeparators(qfi.path())));
+ return ReturnFalse;
+ }
+ QString errStr;
+ if (!doWriteFile(qfi.filePath(), mode, contents, &errStr)) {
+ evalError(fL1S("Cannot write %1file %2: %3.")
+ .arg(ctx, QDir::toNativeSeparators(qfi.filePath()), errStr));
+ return ReturnFalse;
+ }
+ return ReturnTrue;
+}
+
+#ifndef QT_BOOTSTRAPPED
+void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
+{
+ proc->setWorkingDirectory(currentDirectory());
+# ifdef PROEVALUATOR_SETENV
+ if (!m_option->environment.isEmpty())
+ proc->setProcessEnvironment(m_option->environment);
+# endif
+# ifdef Q_OS_WIN
+ proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
+ proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
+# else
+ proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
+# endif
+ proc->waitForFinished(-1);
+}
+#endif
+
+QByteArray QMakeEvaluator::getCommandOutput(const QString &args) const
+{
+#ifndef QT_BOOTSTRAPPED
+ QProcess proc;
+ runProcess(&proc, args);
+ QByteArray errout = proc.readAllStandardError();
+# ifdef PROEVALUATOR_FULL
+ // FIXME: Qt really should have the option to set forwarding per channel
+ fputs(errout.constData(), stderr);
+# else
+ if (!errout.isEmpty()) {
+ if (errout.endsWith('\n'))
+ errout.chop(1);
+ m_handler->message(QMakeHandler::EvalError, QString::fromLocal8Bit(errout));
+ }
+# endif
+ return proc.readAllStandardOutput();
+#else
+ QByteArray out;
+ if (FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
+ + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
+ + QLatin1String(" && ") + args).toLocal8Bit().constData(), "r")) {
+ while (!feof(proc)) {
+ char buff[10 * 1024];
+ int read_in = int(fread(buff, 1, sizeof(buff), proc));
+ if (!read_in)
+ break;
+ out += QByteArray(buff, read_in);
+ }
+ QT_PCLOSE(proc);
+ }
+ return out;
+#endif
+}
+
+void QMakeEvaluator::populateDeps(
+ const ProStringList &deps, const ProString &prefix,
+ QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees,
+ ProStringList &rootSet) const
+{
+ foreach (const ProString &item, deps)
+ if (!dependencies.contains(item.toKey())) {
+ QSet<ProKey> &dset = dependencies[item.toKey()]; // Always create entry
+ ProStringList depends = values(ProKey(prefix + item + QString::fromLatin1(".depends")));
+ if (depends.isEmpty()) {
+ rootSet << item;
+ } else {
+ foreach (const ProString &dep, depends) {
+ dset.insert(dep.toKey());
+ dependees[dep.toKey()] << item;
+ }
+ populateDeps(depends, prefix, dependencies, dependees, rootSet);
+ }
+ }
+}
+
+ProStringList QMakeEvaluator::evaluateBuiltinExpand(
+ const ProKey &func, const ProStringList &args)
+{
+ ProStringList ret;
+
+ traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args));
+
+ ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
+ if (func_t == 0) {
+ const QString &fn = func.toQString(m_tmp1);
+ const QString &lfn = fn.toLower();
+ if (!fn.isSharedWith(lfn)) {
+ func_t = ExpandFunc(statics.expands.value(ProKey(lfn)));
+ if (func_t)
+ deprecationWarning(fL1S("Using uppercased builtin functions is deprecated."));
+ }
+ }
+ switch (func_t) {
+ case E_BASENAME:
+ case E_DIRNAME:
+ case E_SECTION: {
+ bool regexp = false;
+ QString sep;
+ ProString var;
+ int beg = 0;
+ int end = -1;
+ if (func_t == E_SECTION) {
+ if (args.count() != 3 && args.count() != 4) {
+ evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
+ " three or four arguments.").arg(func.toQString(m_tmp1)));
+ } else {
+ var = args[0];
+ sep = args.at(1).toQString();
+ beg = args.at(2).toQString(m_tmp2).toInt();
+ if (args.count() == 4)
+ end = args.at(3).toQString(m_tmp2).toInt();
+ }
+ } else {
+ if (args.count() != 1) {
+ evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
+ } else {
+ var = args[0];
+ regexp = true;
+ sep = QLatin1String("[\\\\/]");
+ if (func_t == E_DIRNAME)
+ end = -2;
+ else
+ beg = -1;
+ }
+ }
+ if (!var.isEmpty()) {
+ if (regexp) {
+ QRegExp sepRx(sep);
+ foreach (const ProString &str, values(map(var))) {
+ const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
+ ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str));
+ }
+ } else {
+ foreach (const ProString &str, values(map(var))) {
+ const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
+ ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str));
+ }
+ }
+ }
+ break;
+ }
+ case E_SPRINTF:
+ if (args.count() < 1) {
+ evalError(fL1S("sprintf(format, ...) requires at least one argument."));
+ } else {
+ QString tmp = args.at(0).toQString(m_tmp1);
+ for (int i = 1; i < args.count(); ++i)
+ tmp = tmp.arg(args.at(i).toQString(m_tmp2));
+ // Note: this depends on split_value_list() making a deep copy
+ ret = split_value_list(tmp);
+ }
+ break;
+ case E_FORMAT_NUMBER:
+ if (args.count() > 2) {
+ evalError(fL1S("format_number(number[, options...]) requires one or two arguments."));
+ } else {
+ int ibase = 10;
+ int obase = 10;
+ int width = 0;
+ bool zeropad = false;
+ bool leftalign = false;
+ enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
+ if (args.count() >= 2) {
+ foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) {
+ opt.toQString(m_tmp3);
+ if (m_tmp3.startsWith(QLatin1String("ibase="))) {
+ ibase = m_tmp3.mid(6).toInt();
+ } else if (m_tmp3.startsWith(QLatin1String("obase="))) {
+ obase = m_tmp3.mid(6).toInt();
+ } else if (m_tmp3.startsWith(QLatin1String("width="))) {
+ width = m_tmp3.mid(6).toInt();
+ } else if (m_tmp3 == QLatin1String("zeropad")) {
+ zeropad = true;
+ } else if (m_tmp3 == QLatin1String("padsign")) {
+ sign = PadSign;
+ } else if (m_tmp3 == QLatin1String("alwayssign")) {
+ sign = AlwaysSign;
+ } else if (m_tmp3 == QLatin1String("leftalign")) {
+ leftalign = true;
+ } else {
+ evalError(fL1S("format_number(): invalid format option %1.").arg(m_tmp3));
+ goto formfail;
+ }
+ }
+ }
+ args.at(0).toQString(m_tmp3);
+ if (m_tmp3.contains(QLatin1Char('.'))) {
+ evalError(fL1S("format_number(): floats are currently not supported."));
+ break;
+ }
+ bool ok;
+ qlonglong num = m_tmp3.toLongLong(&ok, ibase);
+ if (!ok) {
+ evalError(fL1S("format_number(): malformed number %2 for base %1.")
+ .arg(ibase).arg(m_tmp3));
+ break;
+ }
+ QString outstr;
+ if (num < 0) {
+ num = -num;
+ outstr = QLatin1Char('-');
+ } else if (sign == AlwaysSign) {
+ outstr = QLatin1Char('+');
+ } else if (sign == PadSign) {
+ outstr = QLatin1Char(' ');
+ }
+ QString numstr = QString::number(num, obase);
+ int space = width - outstr.length() - numstr.length();
+ if (space <= 0) {
+ outstr += numstr;
+ } else if (leftalign) {
+ outstr += numstr + QString(space, QLatin1Char(' '));
+ } else if (zeropad) {
+ outstr += QString(space, QLatin1Char('0')) + numstr;
+ } else {
+ outstr.prepend(QString(space, QLatin1Char(' ')));
+ outstr += numstr;
+ }
+ ret += ProString(outstr);
+ }
+ formfail:
+ break;
+ case E_JOIN: {
+ if (args.count() < 1 || args.count() > 4) {
+ evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
+ } else {
+ QString glue;
+ ProString before, after;
+ if (args.count() >= 2)
+ glue = args.at(1).toQString(m_tmp1);
+ if (args.count() >= 3)
+ before = args[2];
+ if (args.count() == 4)
+ after = args[3];
+ const ProStringList &var = values(map(args.at(0)));
+ if (!var.isEmpty()) {
+ const ProFile *src = currentProFile();
+ foreach (const ProString &v, var)
+ if (const ProFile *s = v.sourceFile()) {
+ src = s;
+ break;
+ }
+ ret = split_value_list(before + var.join(glue) + after, src);
+ }
+ }
+ break;
+ }
+ case E_SPLIT:
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("split(var, sep) requires one or two arguments."));
+ } else {
+ const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
+ foreach (const ProString &var, values(map(args.at(0))))
+ foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
+ ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt).setSource(var));
+ }
+ break;
+ case E_MEMBER:
+ if (args.count() < 1 || args.count() > 3) {
+ evalError(fL1S("member(var, start, end) requires one to three arguments."));
+ } else {
+ bool ok = true;
+ const ProStringList &var = values(map(args.at(0)));
+ int start = 0, end = 0;
+ if (args.count() >= 2) {
+ const QString &start_str = args.at(1).toQString(m_tmp1);
+ start = start_str.toInt(&ok);
+ if (!ok) {
+ if (args.count() == 2) {
+ int dotdot = start_str.indexOf(statics.strDotDot);
+ if (dotdot != -1) {
+ start = start_str.left(dotdot).toInt(&ok);
+ if (ok)
+ end = start_str.mid(dotdot+2).toInt(&ok);
+ }
+ }
+ if (!ok)
+ evalError(fL1S("member() argument 2 (start) '%2' invalid.")
+ .arg(start_str));
+ } else {
+ end = start;
+ if (args.count() == 3)
+ end = args.at(2).toQString(m_tmp1).toInt(&ok);
+ if (!ok)
+ evalError(fL1S("member() argument 3 (end) '%2' invalid.")
+ .arg(args.at(2).toQString(m_tmp1)));
+ }
+ }
+ if (ok) {
+ if (start < 0)
+ start += var.count();
+ if (end < 0)
+ end += var.count();
+ if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
+ //nothing
+ } else if (start < end) {
+ for (int i = start; i <= end && var.count() >= i; i++)
+ ret.append(var[i]);
+ } else {
+ for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
+ ret += var[i];
+ }
+ }
+ }
+ break;
+ case E_FIRST:
+ case E_LAST:
+ if (args.count() != 1) {
+ evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
+ } else {
+ const ProStringList &var = values(map(args.at(0)));
+ if (!var.isEmpty()) {
+ if (func_t == E_FIRST)
+ ret.append(var[0]);
+ else
+ ret.append(var.last());
+ }
+ }
+ break;
+ case E_SIZE:
+ if (args.count() != 1)
+ evalError(fL1S("size(var) requires one argument."));
+ else
+ ret.append(ProString(QString::number(values(map(args.at(0))).size())));
+ break;
+ case E_CAT:
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
+ } else {
+ const QString &file = args.at(0).toQString(m_tmp1);
+
+ bool blob = false;
+ bool lines = false;
+ bool singleLine = true;
+ if (args.count() > 1) {
+ args.at(1).toQString(m_tmp2);
+ if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive))
+ singleLine = false;
+ else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive))
+ blob = true;
+ else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive))
+ lines = true;
+ }
+
+ QFile qfile(resolvePath(m_option->expandEnvVars(file)));
+ if (qfile.open(QIODevice::ReadOnly)) {
+ QTextStream stream(&qfile);
+ if (blob) {
+ ret += ProString(stream.readAll());
+ } else {
+ while (!stream.atEnd()) {
+ if (lines) {
+ ret += ProString(stream.readLine());
+ } else {
+ ret += split_value_list(stream.readLine().trimmed());
+ if (!singleLine)
+ ret += ProString("\n");
+ }
+ }
+ }
+ }
+ }
+ break;
+ case E_FROMFILE:
+ if (args.count() != 2) {
+ evalError(fL1S("fromfile(file, variable) requires two arguments."));
+ } else {
+ ProValueMap vars;
+ QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+ fn.detach();
+ if (evaluateFileInto(fn, &vars, LoadProOnly))
+ ret = vars.value(map(args.at(1)));
+ }
+ break;
+ case E_EVAL:
+ if (args.count() != 1) {
+ evalError(fL1S("eval(variable) requires one argument."));
+ } else {
+ ret += values(map(args.at(0)));
+ }
+ break;
+ case E_LIST: {
+ QString tmp;
+ tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
+ ret = ProStringList(ProString(tmp));
+ ProStringList lst;
+ foreach (const ProString &arg, args)
+ lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy
+ m_valuemapStack.top()[ret.at(0).toKey()] = lst;
+ break; }
+ case E_FIND:
+ if (args.count() != 2) {
+ evalError(fL1S("find(var, str) requires two arguments."));
+ } else {
+ QRegExp regx(args.at(1).toQString());
+ int t = 0;
+ foreach (const ProString &val, values(map(args.at(0)))) {
+ if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
+ ret += val;
+ t ^= 1;
+ }
+ }
+ break;
+ case E_SYSTEM:
+ if (!m_skipLevel) {
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("system(execute) requires one or two arguments."));
+ } else {
+ bool blob = false;
+ bool lines = false;
+ bool singleLine = true;
+ if (args.count() > 1) {
+ args.at(1).toQString(m_tmp2);
+ if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive))
+ singleLine = false;
+ else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive))
+ blob = true;
+ else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive))
+ lines = true;
+ }
+ QByteArray bytes = getCommandOutput(args.at(0).toQString(m_tmp2));
+ if (lines) {
+ QTextStream stream(bytes);
+ while (!stream.atEnd())
+ ret += ProString(stream.readLine());
+ } else {
+ QString output = QString::fromLocal8Bit(bytes);
+ if (blob) {
+ ret += ProString(output);
+ } else {
+ output.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ if (singleLine)
+ output.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ ret += split_value_list(output);
+ }
+ }
+ }
+ }
+ break;
+ case E_UNIQUE:
+ if (args.count() != 1) {
+ evalError(fL1S("unique(var) requires one argument."));
+ } else {
+ ret = values(map(args.at(0)));
+ ret.removeDuplicates();
+ }
+ break;
+ case E_REVERSE:
+ if (args.count() != 1) {
+ evalError(fL1S("reverse(var) requires one argument."));
+ } else {
+ ProStringList var = values(args.at(0).toKey());
+ for (int i = 0; i < var.size() / 2; i++)
+ qSwap(var[i], var[var.size() - i - 1]);
+ ret += var;
+ }
+ break;
+ case E_QUOTE:
+ ret += args;
+ break;
+ case E_ESCAPE_EXPAND:
+ for (int i = 0; i < args.size(); ++i) {
+ QString str = args.at(i).toQString();
+ QChar *i_data = str.data();
+ int i_len = str.length();
+ for (int x = 0; x < i_len; ++x) {
+ if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
+ if (*(i_data+x+1) == QLatin1Char('\\')) {
+ ++x;
+ } else {
+ struct {
+ char in, out;
+ } mapped_quotes[] = {
+ { 'n', '\n' },
+ { 't', '\t' },
+ { 'r', '\r' },
+ { 0, 0 }
+ };
+ for (int i = 0; mapped_quotes[i].in; ++i) {
+ if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
+ *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
+ if (x < i_len-2)
+ memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
+ --i_len;
+ break;
+ }
+ }
+ }
+ }
+ }
+ ret.append(ProString(QString(i_data, i_len)).setSource(args.at(i)));
+ }
+ break;
+ case E_RE_ESCAPE:
+ for (int i = 0; i < args.size(); ++i) {
+ const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i)));
+ }
+ break;
+ case E_VAL_ESCAPE:
+ if (args.count() != 1) {
+ evalError(fL1S("val_escape(var) requires one argument."));
+ } else {
+ const ProStringList &vals = values(args.at(0).toKey());
+ ret.reserve(vals.size());
+ foreach (const ProString &str, vals)
+ ret += ProString(quoteValue(str));
+ }
+ break;
+ case E_UPPER:
+ case E_LOWER:
+ for (int i = 0; i < args.count(); ++i) {
+ QString rstr = args.at(i).toQString(m_tmp1);
+ rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i)));
+ }
+ break;
+ case E_FILES:
+ if (args.count() != 1 && args.count() != 2) {
+ evalError(fL1S("files(pattern, recursive=false) requires one or two arguments."));
+ } else {
+ bool recursive = false;
+ if (args.count() == 2)
+ recursive = isTrue(args.at(1), m_tmp2);
+ QStringList dirs;
+ QString r = m_option->expandEnvVars(args.at(0).toQString(m_tmp1))
+ .replace(QLatin1Char('\\'), QLatin1Char('/'));
+ QString pfx;
+ if (IoUtils::isRelativePath(r)) {
+ pfx = currentDirectory();
+ if (!pfx.endsWith(QLatin1Char('/')))
+ pfx += QLatin1Char('/');
+ }
+ int slash = r.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1) {
+ dirs.append(r.left(slash+1));
+ r = r.mid(slash+1);
+ } else {
+ dirs.append(QString());
+ }
+
+ r.detach(); // Keep m_tmp out of QRegExp's cache
+ QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
+ for (int d = 0; d < dirs.count(); d++) {
+ QString dir = dirs[d];
+ QDir qdir(pfx + dir);
+ for (int i = 0; i < (int)qdir.count(); ++i) {
+ if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
+ continue;
+ QString fname = dir + qdir[i];
+ if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
+ if (recursive)
+ dirs.append(fname + QLatin1Char('/'));
+ }
+ if (regex.exactMatch(qdir[i]))
+ ret += ProString(fname).setSource(currentProFile());
+ }
+ }
+ }
+ break;
+#ifdef PROEVALUATOR_FULL
+ case E_PROMPT: {
+ if (args.count() != 1) {
+ evalError(fL1S("prompt(question) requires one argument."));
+// } else if (currentFileName() == QLatin1String("-")) {
+// evalError(fL1S("prompt(question) cannot be used when '-o -' is used"));
+ } else {
+ QString msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp1));
+ if (!msg.endsWith(QLatin1Char('?')))
+ msg += QLatin1Char('?');
+ fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg));
+
+ QFile qfile;
+ if (qfile.open(stdin, QIODevice::ReadOnly)) {
+ QTextStream t(&qfile);
+ ret = split_value_list(t.readLine());
+ }
+ }
+ break; }
+#endif
+ case E_REPLACE:
+ if (args.count() != 3 ) {
+ evalError(fL1S("replace(var, before, after) requires three arguments."));
+ } else {
+ const QRegExp before(args.at(1).toQString());
+ const QString &after(args.at(2).toQString(m_tmp2));
+ foreach (const ProString &val, values(map(args.at(0)))) {
+ QString rstr = val.toQString(m_tmp1);
+ QString copy = rstr; // Force a detach on modify
+ rstr.replace(before, after);
+ ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr).setSource(val));
+ }
+ }
+ break;
+ case E_SORT_DEPENDS:
+ case E_RESOLVE_DEPENDS:
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("%1(var, prefix) requires one or two arguments.").arg(func.toQString(m_tmp1)));
+ } else {
+ QHash<ProKey, QSet<ProKey> > dependencies;
+ ProValueMap dependees;
+ ProStringList rootSet;
+ ProStringList orgList = values(args.at(0).toKey());
+ populateDeps(orgList, (args.count() < 2 ? ProString() : args.at(1)),
+ dependencies, dependees, rootSet);
+ for (int i = 0; i < rootSet.size(); ++i) {
+ const ProString &item = rootSet.at(i);
+ if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
+ ret.prepend(item);
+ foreach (const ProString &dep, dependees[item.toKey()]) {
+ QSet<ProKey> &dset = dependencies[dep.toKey()];
+ dset.remove(rootSet.at(i).toKey()); // *Don't* use 'item' - rootSet may have changed!
+ if (dset.isEmpty())
+ rootSet << dep;
+ }
+ }
+ }
+ break;
+ case E_ENUMERATE_VARS: {
+ QSet<ProString> keys;
+ foreach (const ProValueMap &vmap, m_valuemapStack)
+ for (ProValueMap::ConstIterator it = vmap.constBegin(); it != vmap.constEnd(); ++it)
+ keys.insert(it.key());
+ ret.reserve(keys.size());
+ foreach (const ProString &key, keys)
+ ret << key;
+ break; }
+ case E_SHADOWED:
+ if (args.count() != 1) {
+ evalError(fL1S("shadowed(path) requires one argument."));
+ } else {
+ QString rstr = m_option->shadowedPath(resolvePath(args.at(0).toQString(m_tmp1)));
+ if (rstr.isEmpty())
+ break;
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_ABSOLUTE_PATH:
+ if (args.count() > 2) {
+ evalError(fL1S("absolute_path(path[, base]) requires one or two arguments."));
+ } else {
+ QString rstr = QDir::cleanPath(
+ QDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory())
+ .absoluteFilePath(args.at(0).toQString(m_tmp1)));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_RELATIVE_PATH:
+ if (args.count() > 2) {
+ evalError(fL1S("relative_path(path[, base]) requires one or two arguments."));
+ } else {
+ QDir baseDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory());
+ QString rstr = baseDir.relativeFilePath(baseDir.absoluteFilePath(
+ args.at(0).toQString(m_tmp1)));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_CLEAN_PATH:
+ if (args.count() != 1) {
+ evalError(fL1S("clean_path(path) requires one argument."));
+ } else {
+ QString rstr = QDir::cleanPath(args.at(0).toQString(m_tmp1));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_SYSTEM_PATH:
+ if (args.count() != 1) {
+ evalError(fL1S("system_path(path) requires one argument."));
+ } else {
+ QString rstr = args.at(0).toQString(m_tmp1);
+#ifdef Q_OS_WIN
+ rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
+#else
+ rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
+#endif
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_SHELL_PATH:
+ if (args.count() != 1) {
+ evalError(fL1S("shell_path(path) requires one argument."));
+ } else {
+ QString rstr = args.at(0).toQString(m_tmp1);
+ if (m_dirSep.startsWith(QLatin1Char('\\')))
+ rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ else
+ rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_SYSTEM_QUOTE:
+ if (args.count() != 1) {
+ evalError(fL1S("system_quote(arg) requires one argument."));
+ } else {
+ QString rstr = IoUtils::shellQuote(args.at(0).toQString(m_tmp1));
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_SHELL_QUOTE:
+ if (args.count() != 1) {
+ evalError(fL1S("shell_quote(arg) requires one argument."));
+ } else {
+ QString rstr = args.at(0).toQString(m_tmp1);
+ if (m_dirSep.startsWith(QLatin1Char('\\')))
+ rstr = IoUtils::shellQuoteWin(rstr);
+ else
+ rstr = IoUtils::shellQuoteUnix(rstr);
+ ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+ }
+ break;
+ case E_INVALID:
+ evalError(fL1S("'%1' is not a recognized replace function.")
+ .arg(func.toQString(m_tmp1)));
+ break;
+ default:
+ evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1)));
+ break;
+ }
+
+ return ret;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
+ const ProKey &function, const ProStringList &args)
+{
+ traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args));
+
+ TestFunc func_t = (TestFunc)statics.functions.value(function);
+ switch (func_t) {
+ case T_DEFINED: {
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
+ " requires one or two arguments."));
+ return ReturnFalse;
+ }
+ const ProKey &var = args.at(0).toKey();
+ if (args.count() > 1) {
+ if (args[1] == QLatin1String("test")) {
+ return returnBool(m_functionDefs.testFunctions.contains(var));
+ } else if (args[1] == QLatin1String("replace")) {
+ return returnBool(m_functionDefs.replaceFunctions.contains(var));
+ } else if (args[1] == QLatin1String("var")) {
+ ProValueMap::Iterator it;
+ return returnBool(findValues(var, &it));
+ }
+ evalError(fL1S("defined(function, type): unexpected type [%1].")
+ .arg(args.at(1).toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ return returnBool(m_functionDefs.replaceFunctions.contains(var)
+ || m_functionDefs.testFunctions.contains(var));
+ }
+ case T_RETURN:
+ m_returnValue = args;
+ // It is "safe" to ignore returns - due to qmake brokeness
+ // they cannot be used to terminate loops anyway.
+ if (m_cumulative)
+ return ReturnTrue;
+ if (m_valuemapStack.size() == 1) {
+ evalError(fL1S("unexpected return()."));
+ return ReturnFalse;
+ }
+ return ReturnReturn;
+ case T_EXPORT: {
+ if (args.count() != 1) {
+ evalError(fL1S("export(variable) requires one argument."));
+ return ReturnFalse;
+ }
+ const ProKey &var = map(args.at(0));
+ for (ProValueMapStack::Iterator vmi = m_valuemapStack.end();
+ --vmi != m_valuemapStack.begin(); ) {
+ ProValueMap::Iterator it = (*vmi).find(var);
+ if (it != (*vmi).end()) {
+ if (it->constBegin() == statics.fakeValue.constBegin()) {
+ // This is stupid, but qmake doesn't propagate deletions
+ m_valuemapStack.first()[var] = ProStringList();
+ } else {
+ m_valuemapStack.first()[var] = *it;
+ }
+ (*vmi).erase(it);
+ while (--vmi != m_valuemapStack.begin())
+ (*vmi).remove(var);
+ break;
+ }
+ }
+ return ReturnTrue;
+ }
+ case T_INFILE:
+ if (args.count() < 2 || args.count() > 3) {
+ evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
+ } else {
+ ProValueMap vars;
+ QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+ fn.detach();
+ if (!evaluateFileInto(fn, &vars, LoadProOnly))
+ return ReturnFalse;
+ if (args.count() == 2)
+ return returnBool(vars.contains(map(args.at(1))));
+ QRegExp regx;
+ const QString &qry = args.at(2).toQString(m_tmp1);
+ if (qry != QRegExp::escape(qry)) {
+ QString copy = qry;
+ copy.detach();
+ regx.setPattern(copy);
+ }
+ int t = 0;
+ foreach (const ProString &s, vars.value(map(args.at(1)))) {
+ if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
+ return ReturnTrue;
+ t ^= 1;
+ }
+ }
+ return ReturnFalse;
+#ifdef PROEVALUATOR_FULL
+ case T_REQUIRES:
+ checkRequirements(args);
+ return ReturnFalse; // Another qmake breakage
+#endif
+ case T_EVAL: {
+ VisitReturn ret = ReturnFalse;
+ ProFile *pro = m_parser->parsedProBlock(args.join(statics.field_sep),
+ m_current.pro->fileName(), m_current.line);
+ if (pro) {
+ if (m_cumulative || pro->isOk()) {
+ m_locationStack.push(m_current);
+ visitProBlock(pro, pro->tokPtr());
+ ret = ReturnTrue; // This return value is not too useful, but that's qmake
+ m_current = m_locationStack.pop();
+ }
+ pro->deref();
+ }
+ return ret;
+ }
+ case T_BREAK:
+ if (m_skipLevel)
+ return ReturnFalse;
+ if (m_loopLevel)
+ return ReturnBreak;
+ evalError(fL1S("Unexpected break()."));
+ return ReturnFalse;
+ case T_NEXT:
+ if (m_skipLevel)
+ return ReturnFalse;
+ if (m_loopLevel)
+ return ReturnNext;
+ evalError(fL1S("Unexpected next()."));
+ return ReturnFalse;
+ case T_IF: {
+ if (args.count() != 1) {
+ evalError(fL1S("if(condition) requires one argument."));
+ return ReturnFalse;
+ }
+ return returnBool(evaluateConditional(args.at(0).toQString(),
+ m_current.pro->fileName(), m_current.line));
+ }
+ case T_CONFIG: {
+ if (args.count() < 1 || args.count() > 2) {
+ evalError(fL1S("CONFIG(config) requires one or two arguments."));
+ return ReturnFalse;
+ }
+ if (args.count() == 1)
+ return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
+ const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
+ const ProStringList &configs = values(statics.strCONFIG);
+
+ for (int i = configs.size() - 1; i >= 0; i--) {
+ for (int mut = 0; mut < mutuals.count(); mut++) {
+ if (configs[i] == mutuals[mut].trimmed()) {
+ return returnBool(configs[i] == args[0]);
+ }
+ }
+ }
+ return ReturnFalse;
+ }
+ case T_CONTAINS: {
+ if (args.count() < 2 || args.count() > 3) {
+ evalError(fL1S("contains(var, val) requires two or three arguments."));
+ return ReturnFalse;
+ }
+
+ const QString &qry = args.at(1).toQString(m_tmp1);
+ QRegExp regx;
+ if (qry != QRegExp::escape(qry)) {
+ QString copy = qry;
+ copy.detach();
+ regx.setPattern(copy);
+ }
+ const ProStringList &l = values(map(args.at(0)));
+ if (args.count() == 2) {
+ int t = 0;
+ for (int i = 0; i < l.size(); ++i) {
+ const ProString &val = l[i];
+ if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
+ return ReturnTrue;
+ t ^= 1;
+ }
+ } else {
+ const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
+ for (int i = l.size() - 1; i >= 0; i--) {
+ const ProString val = l[i];
+ for (int mut = 0; mut < mutuals.count(); mut++) {
+ if (val == mutuals[mut].trimmed()) {
+ return returnBool((!regx.isEmpty()
+ && regx.exactMatch(val.toQString(m_tmp2)))
+ || val == qry);
+ }
+ }
+ }
+ }
+ return ReturnFalse;
+ }
+ case T_COUNT: {
+ if (args.count() != 2 && args.count() != 3) {
+ evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
+ return ReturnFalse;
+ }
+ int cnt = values(map(args.at(0))).count();
+ if (args.count() == 3) {
+ const ProString &comp = args.at(2);
+ const int val = args.at(1).toQString(m_tmp1).toInt();
+ if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
+ return returnBool(cnt > val);
+ } else if (comp == QLatin1String(">=")) {
+ return returnBool(cnt >= val);
+ } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
+ return returnBool(cnt < val);
+ } else if (comp == QLatin1String("<=")) {
+ return returnBool(cnt <= val);
+ } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
+ || comp == QLatin1String("=") || comp == QLatin1String("==")) {
+ return returnBool(cnt == val);
+ } else {
+ evalError(fL1S("Unexpected modifier to count(%2).").arg(comp.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ }
+ return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
+ }
+ case T_GREATERTHAN:
+ case T_LESSTHAN: {
+ if (args.count() != 2) {
+ evalError(fL1S("%1(variable, value) requires two arguments.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ const QString &rhs(args.at(1).toQString(m_tmp1)),
+ &lhs(values(map(args.at(0))).join(statics.field_sep));
+ bool ok;
+ int rhs_int = rhs.toInt(&ok);
+ if (ok) { // do integer compare
+ int lhs_int = lhs.toInt(&ok);
+ if (ok) {
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs_int > rhs_int);
+ return returnBool(lhs_int < rhs_int);
+ }
+ }
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs > rhs);
+ return returnBool(lhs < rhs);
+ }
+ case T_EQUALS:
+ if (args.count() != 2) {
+ evalError(fL1S("%1(variable, value) requires two arguments.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ return returnBool(values(map(args.at(0))).join(statics.field_sep)
+ == args.at(1).toQString(m_tmp1));
+ case T_CLEAR: {
+ if (args.count() != 1) {
+ evalError(fL1S("%1(variable) requires one argument.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ ProValueMap *hsh;
+ ProValueMap::Iterator it;
+ const ProKey &var = map(args.at(0));
+ if (!(hsh = findValues(var, &it)))
+ return ReturnFalse;
+ if (hsh == &m_valuemapStack.top())
+ it->clear();
+ else
+ m_valuemapStack.top()[var].clear();
+ return ReturnTrue;
+ }
+ case T_UNSET: {
+ if (args.count() != 1) {
+ evalError(fL1S("%1(variable) requires one argument.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ ProValueMap *hsh;
+ ProValueMap::Iterator it;
+ const ProKey &var = map(args.at(0));
+ if (!(hsh = findValues(var, &it)))
+ return ReturnFalse;
+ if (m_valuemapStack.size() == 1)
+ hsh->erase(it);
+ else if (hsh == &m_valuemapStack.top())
+ *it = statics.fakeValue;
+ else
+ m_valuemapStack.top()[var] = statics.fakeValue;
+ return ReturnTrue;
+ }
+ case T_INCLUDE: {
+ if (args.count() < 1 || args.count() > 3) {
+ evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments."));
+ return ReturnFalse;
+ }
+ QString parseInto;
+ LoadFlags flags = 0;
+ if (args.count() >= 2) {
+ parseInto = args.at(1).toQString(m_tmp2);
+ if (args.count() >= 3 && isTrue(args.at(2), m_tmp3))
+ flags = LoadSilent;
+ }
+ QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+ fn.detach();
+ bool ok;
+ if (parseInto.isEmpty()) {
+ ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags);
+ } else {
+ ProValueMap symbols;
+ if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags))) {
+ ProValueMap newMap;
+ for (ProValueMap::ConstIterator
+ it = m_valuemapStack.top().constBegin(),
+ end = m_valuemapStack.top().constEnd();
+ it != end; ++it) {
+ const QString &ky = it.key().toQString(m_tmp1);
+ if (!(ky.startsWith(parseInto) &&
+ (ky.length() == parseInto.length()
+ || ky.at(parseInto.length()) == QLatin1Char('.'))))
+ newMap[it.key()] = it.value();
+ }
+ for (ProValueMap::ConstIterator it = symbols.constBegin();
+ it != symbols.constEnd(); ++it) {
+ const QString &ky = it.key().toQString(m_tmp1);
+ if (!ky.startsWith(QLatin1Char('.')))
+ newMap.insert(ProKey(parseInto + QLatin1Char('.') + ky), it.value());
+ }
+ m_valuemapStack.top() = newMap;
+ }
+ }
+ return returnBool(ok || (flags & LoadSilent));
+ }
+ case T_LOAD: {
+ bool ignore_error = false;
+ if (args.count() == 2) {
+ ignore_error = isTrue(args.at(1), m_tmp2);
+ } else if (args.count() != 1) {
+ evalError(fL1S("load(feature) requires one or two arguments."));
+ return ReturnFalse;
+ }
+ return returnBool(evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()),
+ ignore_error) || ignore_error);
+ }
+ case T_DEBUG: {
+#ifdef PROEVALUATOR_DEBUG
+ if (args.count() != 2) {
+ evalError(fL1S("debug(level, message) requires two arguments."));
+ return ReturnFalse;
+ }
+ int level = args.at(0).toInt();
+ if (level <= m_debugLevel) {
+ const QString &msg = m_option->expandEnvVars(args.at(1).toQString(m_tmp2));
+ debugMsg(level, "Project DEBUG: %s", qPrintable(msg));
+ }
+#endif
+ return ReturnTrue;
+ }
+ case T_LOG:
+ case T_ERROR:
+ case T_WARNING:
+ case T_MESSAGE: {
+ if (args.count() != 1) {
+ evalError(fL1S("%1(message) requires one argument.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ const QString &msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp2));
+ if (!m_skipLevel) {
+ if (func_t == T_LOG) {
+#ifdef PROEVALUATOR_FULL
+ fputs(msg.toLatin1().constData(), stderr);
+#endif
+ } else {
+ m_handler->fileMessage(fL1S("Project %1: %2")
+ .arg(function.toQString(m_tmp1).toUpper(), msg));
+ }
+ }
+ return (func_t == T_ERROR && !m_cumulative) ? ReturnError : ReturnTrue;
+ }
+#ifdef PROEVALUATOR_FULL
+ case T_SYSTEM: {
+ if (m_cumulative) // Anything else would be insanity
+ return ReturnFalse;
+ if (args.count() != 1) {
+ evalError(fL1S("system(exec) requires one argument."));
+ return ReturnFalse;
+ }
+#ifndef QT_BOOTSTRAPPED
+ QProcess proc;
+ proc.setProcessChannelMode(QProcess::ForwardedChannels);
+ runProcess(&proc, args.at(0).toQString(m_tmp2));
+ return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
+#else
+ return returnBool(system((QLatin1String("cd ")
+ + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
+ + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
+#endif
+ }
+#endif
+ case T_ISEMPTY: {
+ if (args.count() != 1) {
+ evalError(fL1S("isEmpty(var) requires one argument."));
+ return ReturnFalse;
+ }
+ return returnBool(values(map(args.at(0))).isEmpty());
+ }
+ case T_EXISTS: {
+ if (args.count() != 1) {
+ evalError(fL1S("exists(file) requires one argument."));
+ return ReturnFalse;
+ }
+ const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+
+ if (IoUtils::exists(file)) {
+ return ReturnTrue;
+ }
+ int slsh = file.lastIndexOf(QLatin1Char('/'));
+ QString fn = file.mid(slsh+1);
+ if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
+ QString dirstr = file.left(slsh+1);
+ if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
+ return ReturnTrue;
+ }
+
+ return ReturnFalse;
+ }
+#ifdef PROEVALUATOR_FULL
+ case T_MKPATH: {
+ if (args.count() != 1) {
+ evalError(fL1S("mkpath(file) requires one argument."));
+ return ReturnFalse;
+ }
+ const QString &fn = resolvePath(args.at(0).toQString(m_tmp1));
+ if (!QDir::current().mkpath(fn)) {
+ evalError(fL1S("Cannot create directory %1.").arg(QDir::toNativeSeparators(fn)));
+ return ReturnFalse;
+ }
+ return ReturnTrue;
+ }
+ case T_WRITE_FILE: {
+ if (args.count() > 3) {
+ evalError(fL1S("write_file(name, [content var, [append]]) requires one to three arguments."));
+ return ReturnFalse;
+ }
+ QIODevice::OpenMode mode = QIODevice::Truncate;
+ QString contents;
+ if (args.count() >= 2) {
+ const ProStringList &vals = values(args.at(1).toKey());
+ if (!vals.isEmpty())
+ contents = vals.join(fL1S("\n")) + QLatin1Char('\n');
+ if (args.count() >= 3)
+ if (!args.at(2).toQString(m_tmp1).compare(fL1S("append"), Qt::CaseInsensitive))
+ mode = QIODevice::Append;
+ }
+ return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents);
+ }
+ case T_TOUCH: {
+ if (args.count() != 2) {
+ evalError(fL1S("touch(file, reffile) requires two arguments."));
+ return ReturnFalse;
+ }
+ const QString &tfn = resolvePath(args.at(0).toQString(m_tmp1));
+ const QString &rfn = resolvePath(args.at(1).toQString(m_tmp2));
+#ifdef Q_OS_UNIX
+ struct stat st;
+ if (stat(rfn.toLocal8Bit().constData(), &st)) {
+ evalError(fL1S("Cannot stat() reference file %1: %2.").arg(rfn, fL1S(strerror(errno))));
+ return ReturnFalse;
+ }
+ struct utimbuf utb;
+ utb.actime = time(0);
+ utb.modtime = st.st_mtime;
+ if (utime(tfn.toLocal8Bit().constData(), &utb)) {
+ evalError(fL1S("Cannot touch %1: %2.").arg(tfn, fL1S(strerror(errno))));
+ return ReturnFalse;
+ }
+#else
+ HANDLE rHand = CreateFile((wchar_t*)rfn.utf16(),
+ GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (rHand == INVALID_HANDLE_VALUE) {
+ evalError(fL1S("Cannot open() reference file %1: %2.").arg(rfn, windowsErrorCode()));
+ return ReturnFalse;
+ }
+ FILETIME ft;
+ GetFileTime(rHand, 0, 0, &ft);
+ CloseHandle(rHand);
+ HANDLE wHand = CreateFile((wchar_t*)tfn.utf16(),
+ GENERIC_WRITE, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (wHand == INVALID_HANDLE_VALUE) {
+ evalError(fL1S("Cannot open() %1: %2.").arg(tfn, windowsErrorCode()));
+ return ReturnFalse;
+ }
+ SetFileTime(wHand, 0, 0, &ft);
+ CloseHandle(wHand);
+#endif
+ return ReturnTrue;
+ }
+ case T_CACHE: {
+ if (args.count() > 3) {
+ evalError(fL1S("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments."));
+ return ReturnFalse;
+ }
+ bool persist = true;
+ bool super = false;
+ enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
+ ProKey srcvar;
+ if (args.count() >= 2) {
+ foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) {
+ opt.toQString(m_tmp3);
+ if (m_tmp3 == QLatin1String("transient")) {
+ persist = false;
+ } else if (m_tmp3 == QLatin1String("super")) {
+ super = true;
+ } else if (m_tmp3 == QLatin1String("set")) {
+ mode = CacheSet;
+ } else if (m_tmp3 == QLatin1String("add")) {
+ mode = CacheAdd;
+ } else if (m_tmp3 == QLatin1String("sub")) {
+ mode = CacheSub;
+ } else {
+ evalError(fL1S("cache(): invalid flag %1.").arg(m_tmp3));
+ return ReturnFalse;
+ }
+ }
+ if (args.count() >= 3) {
+ srcvar = args.at(2).toKey();
+ } else if (mode != CacheSet) {
+ evalError(fL1S("cache(): modes other than 'set' require a source variable."));
+ return ReturnFalse;
+ }
+ }
+ QString varstr;
+ ProKey dstvar = args.at(0).toKey();
+ if (!dstvar.isEmpty()) {
+ if (srcvar.isEmpty())
+ srcvar = dstvar;
+ ProValueMap::Iterator srcvarIt;
+ if (!findValues(srcvar, &srcvarIt)) {
+ evalError(fL1S("Variable %1 is not defined.").arg(srcvar.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+ // The caches for the host and target may differ (e.g., when we are manipulating
+ // CONFIG), so we cannot compute a common new value for both.
+ const ProStringList &diffval = *srcvarIt;
+ ProStringList newval;
+ bool changed = false;
+ for (bool hostBuild = false; ; hostBuild = true) {
+ if (QMakeBaseEnv *baseEnv = m_option->baseEnvs.value(
+ QMakeBaseKey(m_buildRoot, hostBuild))) {
+ QMakeEvaluator *baseEval = baseEnv->evaluator;
+ const ProStringList &oldval = baseEval->values(dstvar);
+ if (mode == CacheSet) {
+ newval = diffval;
+ } else {
+ newval = oldval;
+ if (mode == CacheAdd)
+ newval += diffval;
+ else
+ removeEach(&newval, diffval);
+ }
+ if (oldval != newval) {
+ baseEval->valuesRef(dstvar) = newval;
+ if (super) {
+ do {
+ if (dstvar == QLatin1String("QMAKEPATH")) {
+ baseEval->m_qmakepath = newval.toQStringList();
+ baseEval->updateMkspecPaths();
+ } else if (dstvar == QLatin1String("QMAKEFEATURES")) {
+ baseEval->m_qmakefeatures = newval.toQStringList();
+ } else {
+ break;
+ }
+ baseEval->updateFeaturePaths();
+ if (hostBuild == m_hostBuild)
+ m_featureRoots = baseEval->m_featureRoots;
+ } while (false);
+ }
+ changed = true;
+ }
+ }
+ if (hostBuild)
+ break;
+ }
+ // We assume that whatever got the cached value to be what it is now will do so
+ // the next time as well, so we just skip the persisting if nothing changed.
+ if (!persist || !changed)
+ return ReturnTrue;
+ varstr = dstvar.toQString();
+ if (mode == CacheAdd)
+ varstr += QLatin1String(" +=");
+ else if (mode == CacheSub)
+ varstr += QLatin1String(" -=");
+ else
+ varstr += QLatin1String(" =");
+ if (diffval.count() == 1) {
+ varstr += QLatin1Char(' ');
+ varstr += quoteValue(diffval.at(0));
+ } else if (!diffval.isEmpty()) {
+ foreach (const ProString &vval, diffval) {
+ varstr += QLatin1String(" \\\n ");
+ varstr += quoteValue(vval);
+ }
+ }
+ varstr += QLatin1Char('\n');
+ }
+ QString fn;
+ if (super) {
+ if (m_superfile.isEmpty()) {
+ m_superfile = m_outputDir + QLatin1String("/.qmake.super");
+ printf("Info: creating super cache file %s\n", qPrintable(m_superfile));
+ valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
+ }
+ fn = m_superfile;
+ } else {
+ if (m_cachefile.isEmpty()) {
+ m_cachefile = m_outputDir + QLatin1String("/.qmake.cache");
+ printf("Info: creating cache file %s\n", qPrintable(m_cachefile));
+ valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
+ // We could update m_{source,build}Root and m_featureRoots here, or even
+ // "re-home" our rootEnv, but this doesn't sound too useful - if somebody
+ // wanted qmake to find something in the build directory, he could have
+ // done so "from the outside".
+ // The sub-projects will find the new cache all by themselves.
+ }
+ fn = m_cachefile;
+ }
+ return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr);
+ }
+#endif
+ case T_INVALID:
+ evalError(fL1S("'%1' is not a recognized test function.")
+ .arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ default:
+ evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp
new file mode 100644
index 0000000000..4376865820
--- /dev/null
+++ b/qmake/library/qmakeevaluator.cpp
@@ -0,0 +1,1981 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeevaluator.h"
+
+#include "qmakeglobals.h"
+#include "qmakeparser.h"
+#include "qmakeevaluator_p.h"
+#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>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace QMakeInternal;
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+
+QMakeBaseKey::QMakeBaseKey(const QString &_root, bool _hostBuild)
+ : root(_root), hostBuild(_hostBuild)
+{
+}
+
+uint qHash(const QMakeBaseKey &key)
+{
+ return qHash(key.root) ^ (uint)key.hostBuild;
+}
+
+bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
+{
+ return one.root == two.root && one.hostBuild == two.hostBuild;
+}
+
+QMakeBaseEnv::QMakeBaseEnv()
+ : evaluator(0)
+{
+#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");
+ statics.strDot = QLatin1String(".");
+ statics.strDotDot = QLatin1String("..");
+ statics.strever = QLatin1String("ever");
+ statics.strforever = QLatin1String("forever");
+ statics.strhost_build = QLatin1String("host_build");
+ statics.strTEMPLATE = ProKey("TEMPLATE");
+#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" },
+ { "IN_PWD", "PWD" }
+ };
+ 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();
+}
+
+
+QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
+ QMakeParser *parser, QMakeHandler *handler)
+ :
+#ifdef PROEVALUATOR_DEBUG
+ m_debugLevel(option->debugLevel),
+#endif
+ m_option(option), m_parser(parser), m_handler(handler)
+{
+ // So that single-threaded apps don't have to call initialize() for now.
+ initStatics();
+
+ // Configuration, more or less
+ m_caller = 0;
+#ifdef PROEVALUATOR_CUMULATIVE
+ m_cumulative = false;
+#endif
+ m_hostBuild = false;
+
+ // Evaluator state
+#ifdef PROEVALUATOR_CUMULATIVE
+ m_skipLevel = 0;
+#endif
+ m_loopLevel = 0;
+ m_listCount = 0;
+ m_valuemapStack.push(ProValueMap());
+ m_valuemapInited = false;
+}
+
+QMakeEvaluator::~QMakeEvaluator()
+{
+}
+
+void QMakeEvaluator::initFrom(const QMakeEvaluator &other)
+{
+ Q_ASSERT_X(&other, "QMakeEvaluator::visitProFile", "Project not prepared");
+ m_functionDefs = other.m_functionDefs;
+ m_valuemapStack = other.m_valuemapStack;
+ m_valuemapInited = true;
+ m_qmakespec = other.m_qmakespec;
+ m_qmakespecFull = other.m_qmakespecFull;
+ m_qmakespecName = other.m_qmakespecName;
+ m_mkspecPaths = other.m_mkspecPaths;
+ m_featureRoots = other.m_featureRoots;
+ m_dirSep = other.m_dirSep;
+}
+
+//////// Evaluator tools /////////
+
+uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
+{
+ uint len = *tokPtr++;
+ len |= (uint)*tokPtr++ << 16;
+ return len;
+}
+
+ProString QMakeEvaluator::getStr(const ushort *&tokPtr)
+{
+ uint len = *tokPtr++;
+ ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len);
+ ret.setSource(m_current.pro);
+ tokPtr += len;
+ return ret;
+}
+
+ProKey QMakeEvaluator::getHashStr(const ushort *&tokPtr)
+{
+ uint hash = getBlockLen(tokPtr);
+ uint len = *tokPtr++;
+ ProKey ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
+ tokPtr += len;
+ return ret;
+}
+
+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.
+ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFile *source)
+{
+ QString build;
+ ProStringList ret;
+ QStack<char> quote;
+
+ const ushort SPACE = ' ';
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+ const ushort BACKSLASH = '\\';
+
+ if (!source)
+ source = currentProFile();
+
+ ushort unicode;
+ const QChar *vals_data = vals.data();
+ const int vals_len = vals.length();
+ int parens = 0;
+ for (int x = 0; x < vals_len; x++) {
+ unicode = vals_data[x].unicode();
+ if (x != (int)vals_len-1 && unicode == BACKSLASH &&
+ (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
+ build += vals_data[x++]; //get that 'escape'
+ } else if (!quote.isEmpty() && unicode == quote.top()) {
+ quote.pop();
+ } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+ quote.push(unicode);
+ } else if (unicode == RPAREN) {
+ --parens;
+ } else if (unicode == LPAREN) {
+ ++parens;
+ }
+
+ if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
+ ret << ProString(build).setSource(source);
+ build.clear();
+ } else {
+ build += vals_data[x];
+ }
+ }
+ if (!build.isEmpty())
+ ret << ProString(build).setSource(source);
+ if (parens)
+ deprecationWarning(fL1S("Unmatched parentheses are deprecated."));
+ return ret;
+}
+
+static void zipEmpty(ProStringList *value)
+{
+ for (int i = value->size(); --i >= 0;)
+ if (value->at(i).isEmpty())
+ value->remove(i);
+}
+
+static void insertUnique(ProStringList *varlist, const ProStringList &value)
+{
+ foreach (const ProString &str, value)
+ if (!str.isEmpty() && !varlist->contains(str))
+ varlist->append(str);
+}
+
+static void removeAll(ProStringList *varlist, const ProString &value)
+{
+ for (int i = varlist->size(); --i >= 0; )
+ if (varlist->at(i) == value)
+ varlist->remove(i);
+}
+
+void QMakeEvaluator::removeEach(ProStringList *varlist, const ProStringList &value)
+{
+ foreach (const ProString &str, value)
+ if (!str.isEmpty())
+ removeAll(varlist, str);
+}
+
+static void replaceInList(ProStringList *varlist,
+ const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
+{
+ for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
+ QString val = varit->toQString(tmp);
+ 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);
+ }
+ }
+ }
+ }
+}
+
+void QMakeEvaluator::evaluateExpression(
+ const ushort *&tokPtr, ProStringList *ret, bool joined)
+{
+ debugMsg(2, joined ? "evaluating joined expression" : "evaluating expression");
+ 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: {
+ const ProString &val = getStr(tokPtr);
+ debugMsg(2, "literal %s", dbgStr(val));
+ addStr(val, ret, pending, joined);
+ break; }
+ case TokHashLiteral: {
+ const ProKey &val = getHashStr(tokPtr);
+ debugMsg(2, "hashed literal %s", dbgStr(val.toString()));
+ addStr(val, ret, pending, joined);
+ break; }
+ case TokVariable: {
+ const ProKey &var = getHashStr(tokPtr);
+ const ProStringList &val = values(map(var));
+ debugMsg(2, "variable %s => %s", dbgKey(var), dbgStrList(val));
+ addStrList(val, tok, ret, pending, joined);
+ break; }
+ case TokProperty: {
+ const ProKey &var = getHashStr(tokPtr);
+ const ProString &val = propertyValue(var);
+ debugMsg(2, "property %s => %s", dbgKey(var), dbgStr(val));
+ addStr(val, ret, pending, joined);
+ break; }
+ case TokEnvVar: {
+ const ProString &var = getStr(tokPtr);
+ const ProStringList &val = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
+ debugMsg(2, "env var %s => %s", dbgStr(var), dbgStrList(val));
+ addStrList(val, tok, ret, pending, joined);
+ break; }
+ case TokFuncName: {
+ const ProKey &func = getHashStr(tokPtr);
+ debugMsg(2, "function %s", dbgKey(func));
+ addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
+ break; }
+ default:
+ debugMsg(2, "evaluated expression => %s", dbgStrList(*ret));
+ tokPtr--;
+ return;
+ }
+ }
+}
+
+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;
+ 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:
+ visitProVariable(tok, curr, tokPtr);
+ 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:
+ if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
+ skipHashStr(tokPtr);
+ uint exprLen = getBlockLen(tokPtr);
+ tokPtr += exprLen;
+ blockLen = getBlockLen(tokPtr);
+ ret = visitProBlock(tokPtr);
+ } else if (okey != or_op) {
+ const ProKey &variable = getHashStr(tokPtr);
+ 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;
+ case TokTestDef:
+ case TokReplaceDef:
+ if (m_cumulative || okey != or_op) {
+ const ProKey &name = getHashStr(tokPtr);
+ 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 {
+ okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true);
+ 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;
+ default: {
+ const ushort *oTokPtr = --tokPtr;
+ evaluateExpression(tokPtr, &curr, false);
+ if (tokPtr != oTokPtr)
+ continue;
+ }
+ 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;
+ ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
+ 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) {
+ infinite = true;
+ } else {
+ const QString &itl = it_list.toQString(m_tmp1);
+ 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) {
+ 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));
+
+ m_loopLevel++;
+ forever {
+ if (infinite) {
+ if (!variable.isEmpty())
+ m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++)));
+ if (index > 1000) {
+ 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:
+ m_loopLevel--;
+
+ traceMsg("done looping");
+
+ if (!variable.isEmpty())
+ m_valuemapStack.top()[variable] = oldVarVal;
+ return ret;
+}
+
+void QMakeEvaluator::visitProVariable(
+ 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."));
+ return;
+ }
+ const ProKey &varName = map(curr.first());
+
+ if (tok == TokReplace) { // ~=
+ // DEFINES ~= s/a/b/?[gqi]
+
+ const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
+ const QString &val = varVal.at(0).toQString(m_tmp1);
+ if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
+ evalError(fL1S("The ~= operator can handle only the s/// function."));
+ return;
+ }
+ QChar sep = val.at(1);
+ QStringList func = val.split(sep);
+ if (func.count() < 3 || func.count() > 4) {
+ evalError(fL1S("The s/// function expects 3 or 4 arguments."));
+ return;
+ }
+
+ 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;
+ }
+ QString pattern = func[1];
+ QString replace = func[2];
+ 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 {
+ ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
+ switch (tok) {
+ default: // whatever - cannot happen
+ case TokAssign: // =
+ zipEmpty(&varVal);
+ if (!m_cumulative) {
+ // FIXME: add check+warning about accidental value removal.
+ // This may be a bit too noisy, though.
+ m_valuemapStack.top()[varName] = varVal;
+ } else {
+ if (!varVal.isEmpty()) {
+ // We are greedy for values. But avoid exponential growth.
+ ProStringList &v = valuesRef(varName);
+ if (v.isEmpty()) {
+ v = varVal;
+ } else {
+ ProStringList old = v;
+ v = varVal;
+ QSet<ProString> has;
+ has.reserve(v.size());
+ foreach (const ProString &s, v)
+ has.insert(s);
+ v.reserve(v.size() + old.size());
+ foreach (const ProString &s, old)
+ if (!has.contains(s))
+ v << s;
+ }
+ }
+ }
+ debugMsg(2, "assigning");
+ break;
+ case TokAppendUnique: // *=
+ insertUnique(&valuesRef(varName), varVal);
+ debugMsg(2, "appending unique");
+ break;
+ case TokAppend: // +=
+ zipEmpty(&varVal);
+ valuesRef(varName) += varVal;
+ debugMsg(2, "appending");
+ break;
+ case TokRemove: // -=
+ if (!m_cumulative) {
+ removeEach(&valuesRef(varName), varVal);
+ } else {
+ // We are stingy with our values, too.
+ }
+ debugMsg(2, "removing");
+ break;
+ }
+ }
+ traceMsg("%s := %s", dbgKey(varName), dbgStrList(values(varName)));
+
+ if (varName == statics.strTEMPLATE)
+ setTemplate();
+#ifdef PROEVALUATOR_FULL
+ else if (varName == statics.strREQUIRES)
+ checkRequirements(values(varName));
+#endif
+}
+
+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()) {
+ QString val = values.first().toQString(m_tmp1);
+ if (!val.startsWith(m_option->user_template_prefix)) {
+ val.prepend(m_option->user_template_prefix);
+ values = ProStringList(ProString(val));
+ }
+ }
+}
+
+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);
+#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));
+
+ QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
+ vars[ProKey("QMAKE_HOST.version")] << ProString(QString::number(ver));
+ ProString verStr;
+ switch (ver) {
+ case QSysInfo::WV_Me: verStr = ProString("WinMe"); break;
+ case QSysInfo::WV_95: verStr = ProString("Win95"); break;
+ case QSysInfo::WV_98: verStr = ProString("Win98"); break;
+ case QSysInfo::WV_NT: verStr = ProString("WinNT"); break;
+ case QSysInfo::WV_2000: verStr = ProString("Win2000"); break;
+ case QSysInfo::WV_2003: verStr = ProString("Win2003"); break;
+ case QSysInfo::WV_XP: verStr = ProString("WinXP"); break;
+ case QSysInfo::WV_VISTA: verStr = ProString("WinVista"); break;
+ default: verStr = ProString("Unknown"); break;
+ }
+ vars[ProKey("QMAKE_HOST.version_string")] << verStr;
+
+ 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
+ QLatin1Char backslash('\\');
+ QString paths = m_option->getEnv(QLatin1String("PATH"));
+ QString vcBin64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
+ if (!vcBin64.endsWith(backslash))
+ vcBin64.append(backslash);
+ vcBin64.append(QLatin1String("bin\\amd64"));
+ QString vcBinX86_64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
+ if (!vcBinX86_64.endsWith(backslash))
+ vcBinX86_64.append(backslash);
+ vcBinX86_64.append(QLatin1String("bin\\x86_amd64"));
+ if (paths.contains(vcBin64, Qt::CaseInsensitive)
+ || paths.contains(vcBinX86_64, Qt::CaseInsensitive))
+ vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86_64");
+ else
+ vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86");
+# endif
+#elif defined(Q_OS_UNIX)
+ struct utsname name;
+ if (!uname(&name)) {
+ 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)
+{
+ 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");
+ if (IoUtils::exists(superfile)) {
+ m_superfile = superfile;
+ break;
+ }
+ QFileInfo qdfi(superdir);
+ if (qdfi.isRoot()) {
+ superdir.clear();
+ break;
+ }
+ superdir = qdfi.path();
+ }
+ QString sdir = inDir;
+ QString dir = m_outputDir;
+ forever {
+ conffile = sdir + QLatin1String("/.qmake.conf");
+ if (!IoUtils::exists(conffile))
+ conffile.clear();
+ cachefile = dir + QLatin1String("/.qmake.cache");
+ if (!IoUtils::exists(cachefile))
+ cachefile.clear();
+ if (!conffile.isEmpty() || !cachefile.isEmpty()) {
+ if (dir != sdir)
+ m_sourceRoot = sdir;
+ m_buildRoot = dir;
+ break;
+ }
+ if (dir == superdir)
+ goto no_cache;
+ QFileInfo qsdfi(sdir);
+ QFileInfo qdfi(dir);
+ if (qsdfi.isRoot() || qdfi.isRoot())
+ goto no_cache;
+ sdir = qsdfi.path();
+ dir = qdfi.path();
+ }
+ } else {
+ m_buildRoot = QFileInfo(cachefile).path();
+ }
+ m_conffile = conffile;
+ m_cachefile = cachefile;
+ }
+ no_cache:
+
+ // Look for mkspecs/ in source and build. First to win determines the root.
+ QString sdir = inDir;
+ QString dir = m_outputDir;
+ while (dir != m_buildRoot) {
+ if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir())
+ || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) {
+ if (dir != sdir)
+ m_sourceRoot = sdir;
+ m_buildRoot = dir;
+ break;
+ }
+ if (dir == superdir)
+ break;
+ QFileInfo qsdfi(sdir);
+ QFileInfo qdfi(dir);
+ if (qsdfi.isRoot() || qdfi.isRoot())
+ break;
+ sdir = qsdfi.path();
+ dir = qdfi.path();
+ }
+
+ return true;
+}
+
+bool QMakeEvaluator::loadSpecInternal()
+{
+ if (!evaluateFeatureFile(QLatin1String("spec_pre.prf")))
+ return false;
+ QString spec = m_qmakespec + QLatin1String("/qmake.conf");
+ if (!evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+ evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
+ return false;
+ }
+#ifdef Q_OS_UNIX
+ m_qmakespecFull = QFileInfo(m_qmakespec).canonicalFilePath();
+#else
+ // 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"));
+ m_qmakespecFull = orig_spec.isEmpty() ? m_qmakespec : orig_spec.toQString();
+#endif
+ valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespecFull);
+ m_qmakespecName = IoUtils::fileName(m_qmakespecFull).toString();
+ if (!evaluateFeatureFile(QLatin1String("spec_post.prf")))
+ return false;
+ // The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
+ m_dirSep = first(ProKey("QMAKE_DIR_SEP"));
+ return true;
+}
+
+bool QMakeEvaluator::loadSpec()
+{
+ QString qmakespec = m_option->expandEnvVars(
+ m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
+
+ {
+ QMakeEvaluator evaluator(m_option, m_parser, m_handler);
+ if (!m_superfile.isEmpty()) {
+ valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
+ if (!evaluator.evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly))
+ return false;
+ }
+ if (!m_conffile.isEmpty()) {
+ valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
+ if (!evaluator.evaluateFile(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly))
+ return false;
+ }
+ if (!m_cachefile.isEmpty()) {
+ valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
+ if (!evaluator.evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly))
+ return false;
+ }
+ 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())
+ qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
+ if (IoUtils::isRelativePath(qmakespec)) {
+ foreach (const QString &root, m_mkspecPaths) {
+ QString mkspec = root + QLatin1Char('/') + qmakespec;
+ if (IoUtils::exists(mkspec)) {
+ qmakespec = mkspec;
+ goto cool;
+ }
+ }
+ evalError(fL1S("Could not find qmake configuration file %1.").arg(qmakespec));
+ return false;
+ }
+ cool:
+ m_qmakespec = QDir::cleanPath(qmakespec);
+
+ if (!m_superfile.isEmpty()
+ && !evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+ return false;
+ }
+ if (!loadSpecInternal())
+ return false;
+ updateFeaturePaths(); // The spec extends the feature search path, so rebuild the cache.
+ if (!m_conffile.isEmpty()
+ && !evaluateFile(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+ return false;
+ }
+ if (!m_cachefile.isEmpty()
+ && !evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+ return false;
+ }
+ return true;
+}
+
+void QMakeEvaluator::setupProject()
+{
+ setTemplate();
+ ProValueMap &vars = m_valuemapStack.top();
+ vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName());
+ vars[ProKey("_PRO_FILE_")] << ProString(currentFileName());
+ vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory());
+ vars[ProKey("OUT_PWD")] << ProString(m_outputDir);
+}
+
+void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
+{
+ if (!cmds.isEmpty()) {
+ if (ProFile *pro = m_parser->parsedProBlock(cmds, where, -1)) {
+ if (pro->isOk()) {
+ m_locationStack.push(m_current);
+ visitProBlock(pro, pro->tokPtr());
+ m_current = m_locationStack.pop();
+ }
+ pro->deref();
+ }
+ }
+}
+
+void QMakeEvaluator::evaluateConfigFeatures()
+{
+ QSet<QString> processed;
+ forever {
+ bool finished = true;
+ ProStringList configs = values(statics.strCONFIG);
+ for (int i = configs.size() - 1; i >= 0; --i) {
+ QString config = configs.at(i).toQString(m_tmp1).toLower();
+ if (!processed.contains(config)) {
+ config.detach();
+ processed.insert(config);
+ if (evaluateFeatureFile(config, true)) {
+ finished = false;
+ break;
+ }
+ }
+ }
+ if (finished)
+ break;
+ }
+}
+
+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
+ QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_hostBuild)];
+ if (!*baseEnvPtr)
+ *baseEnvPtr = new QMakeBaseEnv;
+ QMakeBaseEnv *baseEnv = *baseEnvPtr;
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+ {
+ 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
+#endif
+ if (!baseEnv->evaluator) {
+#ifdef PROEVALUATOR_THREAD_SAFE
+ baseEnv->inProgress = true;
+ locker.unlock();
+#endif
+
+ QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler);
+ baseEnv->evaluator = baseEval;
+ baseEval->m_superfile = m_superfile;
+ baseEval->m_conffile = m_conffile;
+ baseEval->m_cachefile = m_cachefile;
+ baseEval->m_sourceRoot = m_sourceRoot;
+ baseEval->m_buildRoot = m_buildRoot;
+ baseEval->m_hostBuild = m_hostBuild;
+ bool ok = baseEval->loadSpec();
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+ locker.relock();
+ baseEnv->isOk = ok;
+ baseEnv->inProgress = false;
+ baseEnv->cond.wakeAll();
+#endif
+
+ if (!ok)
+ return ReturnFalse;
+ }
+#ifdef PROEVALUATOR_THREAD_SAFE
+ }
+#endif
+
+ initFrom(*baseEnv->evaluator);
+ } else {
+ if (!m_valuemapInited)
+ loadDefaults();
+ }
+
+#ifdef QT_BUILD_QMAKE
+ for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
+ it != m_extraVars.constEnd(); ++it)
+ m_valuemapStack.first().insert(it.key(), it.value());
+#endif
+
+ m_handler->aboutToEval(currentProFile(), pro, type);
+ m_profileStack.push(pro);
+ valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+ if (flags & LoadPreFiles) {
+ setupProject();
+
+ evaluateFeatureFile(QLatin1String("default_pre.prf"));
+
+ evaluateCommand(m_option->precmds, fL1S("(command line)"));
+
+#ifdef QT_BUILD_QMAKE
+ // After user configs, to override them
+ if (!m_extraConfigs.isEmpty())
+ evaluateCommand("CONFIG += " + m_extraConfigs.join(" "), fL1S("(extra configs)"));
+#endif
+ }
+
+ debugMsg(1, "visiting file %s", qPrintable(pro->fileName()));
+ visitProBlock(pro, pro->tokPtr());
+ debugMsg(1, "done visiting file %s", qPrintable(pro->fileName()));
+
+ if (flags & LoadPostFiles) {
+ evaluateCommand(m_option->postcmds, fL1S("(command line -after)"));
+
+#ifdef QT_BUILD_QMAKE
+ // 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.
+ if (!m_extraConfigs.isEmpty())
+ evaluateCommand("CONFIG += " + m_extraConfigs.join(" "), fL1S("(extra configs)"));
+#endif
+
+ evaluateFeatureFile(QLatin1String("default_post.prf"));
+
+ evaluateConfigFeatures();
+ }
+ m_profileStack.pop();
+ valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+ m_handler->doneWithEval(currentProFile());
+
+ return ReturnTrue;
+}
+
+
+void QMakeEvaluator::updateMkspecPaths()
+{
+ QStringList ret;
+ const QString concat = QLatin1String("/mkspecs");
+
+ foreach (const QString &it, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
+ ret << it + concat;
+
+ foreach (const QString &it, m_qmakepath)
+ 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;
+
+ ret.removeDuplicates();
+ m_mkspecPaths = ret;
+}
+
+void QMakeEvaluator::updateFeaturePaths()
+{
+ QString mkspecs_concat = QLatin1String("/mkspecs");
+ QString features_concat = QLatin1String("/features/");
+
+ QStringList feature_roots;
+
+ foreach (const QString &f, m_option->getPathListEnv(QLatin1String("QMAKEFEATURES")))
+ feature_roots += f;
+
+ feature_roots += m_qmakefeatures;
+
+ feature_roots += m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString(m_mtmp).split(
+ m_option->dirlist_sep, QString::SkipEmptyParts);
+
+ QStringList feature_bases;
+ if (!m_buildRoot.isEmpty())
+ feature_bases << m_buildRoot;
+ if (!m_sourceRoot.isEmpty())
+ feature_bases << m_sourceRoot;
+
+ foreach (const QString &item, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
+ feature_bases << (item + mkspecs_concat);
+
+ foreach (const QString &item, m_qmakepath)
+ feature_bases << (item + mkspecs_concat);
+
+ if (!m_qmakespecFull.isEmpty()) {
+ // The spec is already platform-dependent, so no subdirs here.
+ feature_roots << (m_qmakespecFull + features_concat);
+
+ // Also check directly under the root directory of the mkspecs collection
+ QDir specdir(m_qmakespecFull);
+ 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;
+ }
+ }
+ }
+
+ feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")).toQString(m_mtmp)
+ + mkspecs_concat);
+
+ foreach (const QString &fb, feature_bases) {
+ foreach (const ProString &sfx, values(ProKey("QMAKE_PLATFORM")))
+ 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;
+ foreach (const QString &root, feature_roots)
+ if (IoUtils::exists(root))
+ ret << root;
+ m_featureRoots = ret;
+}
+
+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())
+// evalError(fL1S("Querying unknown property %1").arg(name.toQString(m_mtmp)));
+ return ret;
+}
+
+ProFile *QMakeEvaluator::currentProFile() const
+{
+ if (m_profileStack.count() > 0)
+ return m_profileStack.top();
+ return 0;
+}
+
+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();
+}
+
+bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex)
+{
+ // 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('?')))) {
+ QString cfg = config;
+ cfg.detach(); // Keep m_tmp out of QRegExp's cache
+ QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
+
+ // mkspecs
+ if (re.exactMatch(m_qmakespecName))
+ return true;
+
+ // CONFIG variable
+ int t = 0;
+ foreach (const ProString &configValue, values(statics.strCONFIG)) {
+ if (re.exactMatch(configValue.toQString(m_tmp[t])))
+ return true;
+ t ^= 1;
+ }
+ } else {
+ // mkspecs
+ if (m_qmakespecName == config)
+ return true;
+
+ // CONFIG variable
+ if (values(statics.strCONFIG).contains(ProString(config)))
+ return true;
+ }
+
+ return false;
+}
+
+ProStringList QMakeEvaluator::expandVariableReferences(
+ const ushort *&tokPtr, int sizeHint, bool joined)
+{
+ ProStringList ret;
+ ret.reserve(sizeHint);
+ forever {
+ evaluateExpression(tokPtr, &ret, joined);
+ switch (*tokPtr) {
+ case TokValueTerminator:
+ case TokFuncTerminator:
+ tokPtr++;
+ return ret;
+ case TokArgSeparator:
+ if (joined) {
+ tokPtr++;
+ continue;
+ }
+ // fallthrough
+ default:
+ Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
+ break;
+ }
+ }
+}
+
+QList<ProStringList> QMakeEvaluator::prepareFunctionArgs(const ushort *&tokPtr)
+{
+ QList<ProStringList> args_list;
+ if (*tokPtr != TokFuncTerminator) {
+ for (;; tokPtr++) {
+ ProStringList arg;
+ evaluateExpression(tokPtr, &arg, false);
+ args_list << arg;
+ if (*tokPtr == TokFuncTerminator)
+ break;
+ Q_ASSERT(*tokPtr == TokArgSeparator);
+ }
+ }
+ tokPtr++;
+ return args_list;
+}
+
+ProStringList QMakeEvaluator::evaluateFunction(
+ const ProFunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
+{
+ bool oki;
+ ProStringList ret;
+
+ if (m_valuemapStack.count() >= 100) {
+ evalError(fL1S("Ran into infinite recursion (depth > 100)."));
+ oki = false;
+ } else {
+ m_valuemapStack.push(ProValueMap());
+ m_locationStack.push(m_current);
+ int loopLevel = m_loopLevel;
+ m_loopLevel = 0;
+
+ 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;
+ VisitReturn vr = visitProBlock(func.pro(), func.tokPtr());
+ oki = (vr != ReturnFalse && vr != ReturnError); // True || Return
+ ret = m_returnValue;
+ m_returnValue.clear();
+
+ m_loopLevel = loopLevel;
+ m_current = m_locationStack.pop();
+ m_valuemapStack.pop();
+ }
+ if (ok)
+ *ok = oki;
+ if (oki)
+ return ret;
+ return ProStringList();
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
+ const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
+ const ProString &function)
+{
+ bool ok;
+ ProStringList ret = evaluateFunction(func, argumentsList, &ok);
+ if (ok) {
+ if (ret.isEmpty())
+ return ReturnTrue;
+ if (ret.at(0) != statics.strfalse) {
+ if (ret.at(0) == statics.strtrue)
+ return ReturnTrue;
+ int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
+ if (ok) {
+ if (val)
+ return ReturnTrue;
+ } else {
+ evalError(fL1S("Unexpected return value from test '%1': %2.")
+ .arg(function.toQString(m_tmp1))
+ .arg(ret.join(QLatin1String(" :: "))));
+ }
+ }
+ }
+ return ReturnFalse;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
+ const ProKey &func, const ushort *&tokPtr)
+{
+ QHash<ProKey, ProFunctionDef>::ConstIterator it =
+ m_functionDefs.testFunctions.constFind(func);
+ if (it != m_functionDefs.testFunctions.constEnd()) {
+ const QList<ProStringList> args = prepareFunctionArgs(tokPtr);
+ traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args));
+ return evaluateBoolFunction(*it, args, func);
+ }
+
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinConditional(func, expandVariableReferences(tokPtr, 5, true));
+}
+
+ProStringList QMakeEvaluator::evaluateExpandFunction(
+ const ProKey &func, const ushort *&tokPtr)
+{
+ QHash<ProKey, ProFunctionDef>::ConstIterator it =
+ m_functionDefs.replaceFunctions.constFind(func);
+ if (it != m_functionDefs.replaceFunctions.constEnd()) {
+ const QList<ProStringList> args = prepareFunctionArgs(tokPtr);
+ traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
+ return evaluateFunction(*it, args, 0);
+ }
+
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinExpand(func, expandVariableReferences(tokPtr, 5, true));
+}
+
+bool QMakeEvaluator::evaluateConditional(const QString &cond, const QString &where, int line)
+{
+ bool ret = false;
+ ProFile *pro = m_parser->parsedProBlock(cond, where, line, QMakeParser::TestGrammar);
+ if (pro) {
+ if (pro->isOk()) {
+ m_locationStack.push(m_current);
+ ret = visitProBlock(pro, pro->tokPtr()) == ReturnTrue;
+ m_current = m_locationStack.pop();
+ }
+ pro->deref();
+ }
+ return ret;
+}
+
+#ifdef PROEVALUATOR_FULL
+void QMakeEvaluator::checkRequirements(const ProStringList &deps)
+{
+ ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS"));
+ foreach (const ProString &dep, deps)
+ if (!evaluateConditional(dep.toQString(), m_current.pro->fileName(), m_current.line))
+ failed << dep;
+}
+#endif
+
+ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit)
+{
+ ProValueMapStack::Iterator vmi = m_valuemapStack.end();
+ do {
+ --vmi;
+ ProValueMap::Iterator it = (*vmi).find(variableName);
+ if (it != (*vmi).end()) {
+ if (it->constBegin() == statics.fakeValue.constBegin())
+ return 0;
+ *rit = it;
+ return &(*vmi);
+ }
+ } while (vmi != m_valuemapStack.begin());
+ return 0;
+}
+
+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;
+ }
+ 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());
+ }
+ return m_valuemapStack.top()[variableName];
+}
+
+ProStringList QMakeEvaluator::values(const ProKey &variableName) const
+{
+ ProValueMapStack::ConstIterator vmi = m_valuemapStack.constEnd();
+ do {
+ --vmi;
+ ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
+ if (it != (*vmi).constEnd()) {
+ if (it->constBegin() == statics.fakeValue.constBegin())
+ break;
+ return *it;
+ }
+ } while (vmi != m_valuemapStack.constBegin());
+ return ProStringList();
+}
+
+ProString QMakeEvaluator::first(const ProKey &variableName) const
+{
+ const ProStringList &vals = values(variableName);
+ if (!vals.isEmpty())
+ return vals.first();
+ return ProString();
+}
+
+bool QMakeEvaluator::evaluateFile(
+ const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+ if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
+ m_locationStack.push(m_current);
+ bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
+ m_current = m_locationStack.pop();
+ pro->deref();
+#ifdef PROEVALUATOR_FULL
+ if (ok) {
+ ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
+ ProString ifn(fileName);
+ if (!iif.contains(ifn))
+ iif << ifn;
+ }
+#endif
+ return ok;
+ } else {
+ if (!(flags & LoadSilent) && !IoUtils::exists(fileName))
+ evalError(fL1S("WARNING: Include file %1 not found").arg(fileName));
+ return false;
+ }
+}
+
+bool QMakeEvaluator::evaluateFileChecked(
+ const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+ if (fileName.isEmpty())
+ return false;
+ QMakeEvaluator *ref = this;
+ do {
+ foreach (const ProFile *pf, ref->m_profileStack)
+ if (pf->fileName() == fileName) {
+ evalError(fL1S("Circular inclusion of %1.").arg(fileName));
+ return false;
+ }
+ } while ((ref = ref->m_caller));
+ return evaluateFile(fileName, type, flags);
+}
+
+bool QMakeEvaluator::evaluateFeatureFile(const QString &fileName, bool silent)
+{
+ QString fn = fileName;
+ if (!fn.endsWith(QLatin1String(".prf")))
+ fn += QLatin1String(".prf");
+
+ if (m_featureRoots.isEmpty())
+ updateFeaturePaths();
+ int start_root = 0;
+ QString currFn = currentFileName();
+ if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
+ for (int root = 0; root < m_featureRoots.size(); ++root)
+ if (currFn == m_featureRoots.at(root) + fn) {
+ start_root = root + 1;
+ break;
+ }
+ }
+ for (int root = start_root; root < m_featureRoots.size(); ++root) {
+ QString fname = m_featureRoots.at(root) + fn;
+ if (IoUtils::exists(fname)) {
+ fn = fname;
+ goto cool;
+ }
+ }
+#ifdef QMAKE_BUILTIN_PRFS
+ fn.prepend(QLatin1String(":/qmake/features/"));
+ if (QFileInfo(fn).exists())
+ goto cool;
+#endif
+ if (!silent)
+ evalError(fL1S("Cannot find feature %1").arg(fileName));
+ return false;
+
+ cool:
+ 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));
+ return true;
+ }
+ already.append(afn);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+ bool cumulative = m_cumulative;
+ m_cumulative = false;
+#endif
+
+ // The path is fully normalized already.
+ bool ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+ m_cumulative = cumulative;
+#endif
+ return ok;
+}
+
+bool QMakeEvaluator::evaluateFileInto(const QString &fileName, ProValueMap *values, LoadFlags flags)
+{
+ QMakeEvaluator visitor(m_option, m_parser, m_handler);
+ visitor.m_caller = this;
+ visitor.m_outputDir = m_outputDir;
+ visitor.m_featureRoots = m_featureRoots;
+ if (!visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags))
+ return false;
+ *values = visitor.m_valuemapStack.top();
+#ifdef PROEVALUATOR_FULL
+ ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES");
+ ProStringList &iif = m_valuemapStack.first()[qiif];
+ foreach (const ProString &ifn, values->value(qiif))
+ if (!iif.contains(ifn))
+ iif << ifn;
+#endif
+ return true;
+}
+
+void QMakeEvaluator::message(int type, const QString &msg) const
+{
+ if (!m_skipLevel)
+ m_handler->message(type, msg,
+ 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;
+ // fallthrough
+ default:
+ ret += c;
+ break;
+ }
+ }
+ }
+ if (quote) {
+ ret.prepend(QLatin1Char('"'));
+ ret.append(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas)
+{
+ QString ret;
+
+ foreach (const ProString &str, vals) {
+ if (!ret.isEmpty()) {
+ if (commas)
+ ret += QLatin1Char(',');
+ ret += QLatin1Char(' ');
+ }
+ ret += formatValue(str);
+ }
+ return ret;
+}
+
+QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists)
+{
+ QString ret;
+
+ foreach (const ProStringList &list, lists) {
+ if (!ret.isEmpty())
+ ret += QLatin1String(", ");
+ ret += formatValueList(list);
+ }
+ return ret;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/qmake/library/qmakeevaluator.h b/qmake/library/qmakeevaluator.h
new file mode 100644
index 0000000000..7c0da0c2d4
--- /dev/null
+++ b/qmake/library/qmakeevaluator.h
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEEVALUATOR_H
+#define QMAKEEVALUATOR_H
+
+#if defined(PROEVALUATOR_FULL) && defined(PROEVALUATOR_THREAD_SAFE)
+# error PROEVALUATOR_FULL is incompatible with PROEVALUATOR_THREAD_SAFE due to cache() implementation
+#endif
+
+#include "qmakeparser.h"
+#include "ioutils.h"
+
+#include <qlist.h>
+#include <qlinkedlist.h>
+#include <qset.h>
+#include <qstack.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#ifndef QT_BOOTSTRAPPED
+# include <qprocess.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMakeGlobals;
+
+class QMAKE_EXPORT QMakeHandler : public QMakeParserHandler
+{
+public:
+ enum {
+ SourceEvaluator = 0x10,
+
+ EvalWarnLanguage = SourceEvaluator | WarningMessage | WarnLanguage,
+ EvalWarnDeprecated = SourceEvaluator | WarningMessage | WarnDeprecated,
+
+ EvalError = ErrorMessage | SourceEvaluator
+ };
+
+ // error(), warning() and message() from .pro file
+ virtual void fileMessage(const QString &msg) = 0;
+
+ enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile };
+ virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0;
+ virtual void doneWithEval(ProFile *parent) = 0;
+};
+
+// We use a QLinkedList based stack instead of a QVector based one (QStack), so that
+// the addresses of value maps stay constant. The qmake generators rely on that.
+class QMAKE_EXPORT ProValueMapStack : public QLinkedList<ProValueMap>
+{
+public:
+ inline void push(const ProValueMap &t) { append(t); }
+ inline ProValueMap pop() { return takeLast(); }
+ ProValueMap &top() { return last(); }
+ const ProValueMap &top() const { return last(); }
+};
+
+class QMAKE_EXPORT QMakeEvaluator
+{
+public:
+ enum LoadFlag {
+ LoadProOnly = 0,
+ LoadPreFiles = 1,
+ LoadPostFiles = 2,
+ LoadAll = LoadPreFiles|LoadPostFiles,
+ LoadSilent = 0x10
+ };
+ Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
+
+ static void initStatics();
+ static void initFunctionStatics();
+ QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser,
+ QMakeHandler *handler);
+ ~QMakeEvaluator();
+
+#ifdef QT_BUILD_QMAKE
+ void setExtraVars(const ProValueMap &extraVars) { m_extraVars = extraVars; }
+ void setExtraConfigs(const ProStringList &extraConfigs) { m_extraConfigs = extraConfigs; }
+#endif
+ void setOutputDir(const QString &outputDir) { m_outputDir = outputDir; }
+
+ ProStringList values(const ProKey &variableName) const;
+ ProStringList &valuesRef(const ProKey &variableName);
+ ProString first(const ProKey &variableName) const;
+ ProString propertyValue(const ProKey &val) const;
+
+ ProString dirSep() const { return m_dirSep; }
+ bool isHostBuild() const { return m_hostBuild; }
+
+ enum VisitReturn {
+ ReturnFalse,
+ ReturnTrue,
+ ReturnError,
+ ReturnBreak,
+ ReturnNext,
+ ReturnReturn
+ };
+
+ static ALWAYS_INLINE VisitReturn returnBool(bool b)
+ { return b ? ReturnTrue : ReturnFalse; }
+
+ static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
+ ProString getStr(const ushort *&tokPtr);
+ ProKey getHashStr(const ushort *&tokPtr);
+ void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
+ static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
+ static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
+ void skipExpression(const ushort *&tokPtr);
+
+ void loadDefaults();
+ bool prepareProject(const QString &inDir);
+ bool loadSpecInternal();
+ bool loadSpec();
+ void initFrom(const QMakeEvaluator &other);
+ void setupProject();
+ void evaluateCommand(const QString &cmds, const QString &where);
+ VisitReturn visitProFile(ProFile *pro, QMakeHandler::EvalFileType type,
+ LoadFlags flags);
+ VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
+ VisitReturn visitProBlock(const ushort *tokPtr);
+ VisitReturn visitProLoop(const ProKey &variable, const ushort *exprPtr,
+ const ushort *tokPtr);
+ void visitProFunctionDef(ushort tok, const ProKey &name, const ushort *tokPtr);
+ void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
+
+ ALWAYS_INLINE const ProKey &map(const ProString &var) { return map(var.toKey()); }
+ const ProKey &map(const ProKey &var);
+ ProValueMap *findValues(const ProKey &variableName, ProValueMap::Iterator *it);
+
+ void setTemplate();
+
+ ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
+ ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
+ ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
+
+ QString currentFileName() const;
+ QString currentDirectory() const;
+ ProFile *currentProFile() const;
+ QString resolvePath(const QString &fileName) const
+ { return QMakeInternal::IoUtils::resolvePath(currentDirectory(), fileName); }
+
+ bool evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type,
+ LoadFlags flags);
+ bool evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type,
+ LoadFlags flags);
+ bool evaluateFeatureFile(const QString &fileName, bool silent = false);
+ bool evaluateFileInto(const QString &fileName,
+ ProValueMap *values, // output-only
+ LoadFlags flags);
+ void evaluateConfigFeatures();
+ void message(int type, const QString &msg) const;
+ void evalError(const QString &msg) const
+ { message(QMakeHandler::EvalError, msg); }
+ void languageWarning(const QString &msg) const
+ { message(QMakeHandler::EvalWarnLanguage, msg); }
+ void deprecationWarning(const QString &msg) const
+ { message(QMakeHandler::EvalWarnDeprecated, msg); }
+
+ QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
+ ProStringList evaluateFunction(const ProFunctionDef &func,
+ const QList<ProStringList> &argumentsList, bool *ok);
+ VisitReturn evaluateBoolFunction(const ProFunctionDef &func,
+ const QList<ProStringList> &argumentsList,
+ const ProString &function);
+
+ ProStringList evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr);
+ VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr);
+
+ ProStringList evaluateBuiltinExpand(const ProKey &function, const ProStringList &args);
+ VisitReturn evaluateBuiltinConditional(const ProKey &function, const ProStringList &args);
+
+ bool evaluateConditional(const QString &cond, const QString &where, int line = -1);
+#ifdef PROEVALUATOR_FULL
+ void checkRequirements(const ProStringList &deps);
+#endif
+
+ void updateMkspecPaths();
+ void updateFeaturePaths();
+
+ bool isActiveConfig(const QString &config, bool regex = false);
+
+ void populateDeps(
+ const ProStringList &deps, const ProString &prefix,
+ QHash<ProKey, QSet<ProKey> > &dependencies,
+ ProValueMap &dependees, ProStringList &rootSet) const;
+
+ VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
+ const QString &contents);
+#ifndef QT_BOOTSTRAPPED
+ void runProcess(QProcess *proc, const QString &command) const;
+#endif
+ QByteArray getCommandOutput(const QString &args) const;
+
+ static void removeEach(ProStringList *varlist, const ProStringList &value);
+
+ QMakeEvaluator *m_caller;
+ int m_loopLevel; // To report unexpected break() and next()s
+#ifdef PROEVALUATOR_CUMULATIVE
+ bool m_cumulative;
+ int m_skipLevel;
+#else
+ enum { m_cumulative = 0 };
+ enum { m_skipLevel = 0 };
+#endif
+
+#ifdef PROEVALUATOR_DEBUG
+ void debugMsgInternal(int level, const char *fmt, ...) const;
+ void traceMsgInternal(const char *fmt, ...) const;
+ static QString formatValue(const ProString &val, bool forceQuote = false);
+ static QString formatValueList(const ProStringList &vals, bool commas = false);
+ static QString formatValueListList(const QList<ProStringList> &vals);
+
+ const int m_debugLevel;
+#else
+ ALWAYS_INLINE void debugMsgInternal(int, const char *, ...) const {}
+ ALWAYS_INLINE void traceMsgInternal(const char *, ...) const {}
+
+ enum { m_debugLevel = 0 };
+#endif
+
+ struct Location {
+ Location() : pro(0), line(0) {}
+ Location(ProFile *_pro, ushort _line) : pro(_pro), line(_line) {}
+ void clear() { pro = 0; line = 0; }
+ ProFile *pro;
+ ushort line;
+ };
+
+ Location m_current; // Currently evaluated location
+ QStack<Location> m_locationStack; // All execution location changes
+ QStack<ProFile *> m_profileStack; // Includes only
+
+#ifdef QT_BUILD_QMAKE
+ ProValueMap m_extraVars;
+ ProStringList m_extraConfigs;
+#endif
+ QString m_outputDir;
+
+ int m_listCount;
+ bool m_valuemapInited;
+ bool m_hostBuild;
+ QString m_qmakespec;
+ QString m_qmakespecFull;
+ QString m_qmakespecName;
+ QString m_superfile;
+ QString m_conffile;
+ QString m_cachefile;
+ QString m_sourceRoot;
+ QString m_buildRoot;
+ QStringList m_qmakepath;
+ QStringList m_qmakefeatures;
+ QStringList m_mkspecPaths;
+ QStringList m_featureRoots;
+ ProString m_dirSep;
+ ProFunctionDefs m_functionDefs;
+ ProStringList m_returnValue;
+ ProValueMapStack m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii.
+ QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
+ mutable QString m_mtmp;
+
+ QMakeGlobals *m_option;
+ QMakeParser *m_parser;
+ QMakeHandler *m_handler;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags)
+
+QT_END_NAMESPACE
+
+#endif // QMAKEEVALUATOR_H
diff --git a/qmake/library/qmakeevaluator_p.h b/qmake/library/qmakeevaluator_p.h
new file mode 100644
index 0000000000..4be500acd0
--- /dev/null
+++ b/qmake/library/qmakeevaluator_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEEVALUATOR_P_H
+#define QMAKEEVALUATOR_P_H
+
+#include "proitems.h"
+
+#include <qregexp.h>
+
+#define debugMsg if (!m_debugLevel) {} else debugMsgInternal
+#define traceMsg if (!m_debugLevel) {} else traceMsgInternal
+#ifdef PROEVALUATOR_DEBUG
+# define dbgBool(b) (b ? "true" : "false")
+# define dbgReturn(r) \
+ (r == ReturnError ? "error" : \
+ r == ReturnBreak ? "break" : \
+ r == ReturnNext ? "next" : \
+ r == ReturnReturn ? "return" : \
+ "<invalid>")
+# define dbgKey(s) qPrintable(s.toString().toQString())
+# define dbgStr(s) qPrintable(formatValue(s, true))
+# define dbgStrList(s) qPrintable(formatValueList(s))
+# define dbgSepStrList(s) qPrintable(formatValueList(s, true))
+# define dbgStrListList(s) qPrintable(formatValueListList(s))
+# define dbgQStr(s) dbgStr(ProString(s))
+#else
+# define dbgBool(b) 0
+# define dbgReturn(r) 0
+# define dbgKey(s) 0
+# define dbgStr(s) 0
+# define dbgStrList(s) 0
+# define dbgSepStrList(s) 0
+# define dbgStrListList(s) 0
+# define dbgQStr(s) 0
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QMakeInternal {
+
+struct QMakeStatics {
+ QString field_sep;
+ QString strtrue;
+ QString strfalse;
+ ProKey strCONFIG;
+ ProKey strARGS;
+ QString strDot;
+ QString strDotDot;
+ QString strever;
+ QString strforever;
+ QString strhost_build;
+ ProKey strTEMPLATE;
+#ifdef PROEVALUATOR_FULL
+ ProKey strREQUIRES;
+#endif
+ QHash<ProKey, int> expands;
+ QHash<ProKey, int> functions;
+ QHash<ProKey, ProKey> varMap;
+ ProStringList fakeValue;
+};
+
+extern QMakeStatics statics;
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMAKEEVALUATOR_P_H
diff --git a/qmake/library/qmakeglobals.cpp b/qmake/library/qmakeglobals.cpp
new file mode 100644
index 0000000000..65196f2810
--- /dev/null
+++ b/qmake/library/qmakeglobals.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeglobals.h"
+
+#include "qmakeevaluator.h"
+#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>
+#include <qtextstream.h>
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qthreadpool.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+namespace { // MSVC doesn't seem to know the semantics of "static" ...
+
+static struct {
+ QRegExp reg_variableName;
+} statics;
+
+}
+
+static void initStatics()
+{
+ if (!statics.reg_variableName.isEmpty())
+ return;
+
+ statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
+ statics.reg_variableName.setMinimal(true);
+}
+
+QMakeGlobals::QMakeGlobals()
+{
+ initStatics();
+
+ do_cache = true;
+
+#ifdef PROEVALUATOR_DEBUG
+ debugLevel = 0;
+#endif
+#ifdef Q_OS_WIN
+ dirlist_sep = QLatin1Char(';');
+ dir_sep = QLatin1Char('\\');
+#else
+ dirlist_sep = QLatin1Char(':');
+ dir_sep = QLatin1Char('/');
+#endif
+ qmakespec = getEnv(QLatin1String("QMAKESPEC"));
+}
+
+QMakeGlobals::~QMakeGlobals()
+{
+ qDeleteAll(baseEnvs);
+}
+
+QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
+{
+ QString ret = QDir::cleanPath(spec);
+ if (ret.contains(QLatin1Char('/'))) {
+ QString absRet = QDir(state.pwd).absoluteFilePath(ret);
+ if (QFile::exists(absRet))
+ ret = QDir::cleanPath(absRet);
+ }
+ return ret;
+}
+
+QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
+ QMakeCmdLineParserState &state, QStringList &args, int *pos)
+{
+ enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache } argState = ArgNone;
+ for (; *pos < args.count(); (*pos)++) {
+ QString arg = args.at(*pos);
+ switch (argState) {
+ case ArgConfig:
+ if (state.after)
+ state.postconfigs << arg;
+ else
+ state.preconfigs << arg;
+ break;
+ case ArgSpec:
+ qmakespec = args[*pos] = cleanSpec(state, arg);
+ break;
+ case ArgXSpec:
+ xqmakespec = args[*pos] = cleanSpec(state, arg);
+ break;
+ case ArgTmpl:
+ user_template = arg;
+ break;
+ case ArgTmplPfx:
+ user_template_prefix = arg;
+ break;
+ case ArgCache:
+ cachefile = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg));
+ break;
+ default:
+ if (arg.startsWith(QLatin1Char('-'))) {
+ if (arg == QLatin1String("-after")) {
+ state.after = true;
+ } else if (arg == QLatin1String("-config")) {
+ argState = ArgConfig;
+ } else if (arg == QLatin1String("-nocache")) {
+ do_cache = false;
+ } else if (arg == QLatin1String("-cache")) {
+ argState = ArgCache;
+ } else if (arg == QLatin1String("-platform") || arg == QLatin1String("-spec")) {
+ argState = ArgSpec;
+ } else if (arg == QLatin1String("-xplatform") || arg == QLatin1String("-xspec")) {
+ argState = ArgXSpec;
+ } else if (arg == QLatin1String("-template") || arg == QLatin1String("-t")) {
+ argState = ArgTmpl;
+ } else if (arg == QLatin1String("-template_prefix") || arg == QLatin1String("-tp")) {
+ argState = ArgTmplPfx;
+ } else if (arg == QLatin1String("-win32")) {
+ dir_sep = QLatin1Char('\\');
+ } else if (arg == QLatin1String("-unix")) {
+ dir_sep = QLatin1Char('/');
+ } else {
+ return ArgumentUnknown;
+ }
+ } else if (arg.contains(QLatin1Char('='))) {
+ if (state.after)
+ state.postcmds << arg;
+ else
+ state.precmds << arg;
+ } else {
+ return ArgumentUnknown;
+ }
+ continue;
+ }
+ argState = ArgNone;
+ }
+ if (argState != ArgNone)
+ return ArgumentMalformed;
+ return ArgumentsOk;
+}
+
+void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
+{
+ if (!state.preconfigs.isEmpty())
+ state.precmds << (fL1S("CONFIG += ") + state.preconfigs.join(fL1S(" ")));
+ precmds = state.precmds.join(fL1S("\n"));
+ if (!state.postconfigs.isEmpty())
+ state.postcmds << (fL1S("CONFIG += ") + state.postconfigs.join(fL1S(" ")));
+ postcmds = state.postcmds.join(fL1S("\n"));
+
+ if (xqmakespec.isEmpty())
+ xqmakespec = qmakespec;
+}
+
+void QMakeGlobals::useEnvironment()
+{
+ if (xqmakespec.isEmpty())
+ xqmakespec = getEnv(QLatin1String("XQMAKESPEC"));
+ if (qmakespec.isEmpty()) {
+ qmakespec = getEnv(QLatin1String("QMAKESPEC"));
+ if (xqmakespec.isEmpty())
+ xqmakespec = qmakespec;
+ }
+}
+
+void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args)
+{
+ QStringList args = _args;
+
+ QMakeCmdLineParserState state(pwd);
+ for (int pos = 0; pos < args.size(); pos++)
+ addCommandLineArguments(state, args, &pos);
+ commitCommandLineArguments(state);
+ useEnvironment();
+}
+
+void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir)
+{
+ if (input_dir != output_dir && !output_dir.isEmpty()) {
+ QString srcpath = input_dir;
+ if (!srcpath.endsWith(QLatin1Char('/')))
+ srcpath += QLatin1Char('/');
+ QString dstpath = output_dir;
+ if (!dstpath.endsWith(QLatin1Char('/')))
+ dstpath += QLatin1Char('/');
+ int srcLen = srcpath.length();
+ int dstLen = dstpath.length();
+ int lastSl = -1;
+ while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen))
+ if (srcpath.at(srcLen) == QLatin1Char('/'))
+ lastSl = 0;
+ source_root = srcpath.left(srcLen + lastSl);
+ build_root = dstpath.left(dstLen + lastSl);
+ }
+}
+
+QString QMakeGlobals::shadowedPath(const QString &fileName) const
+{
+ if (source_root.isEmpty())
+ return fileName;
+ if (fileName.startsWith(source_root)
+ && (fileName.length() == source_root.length()
+ || fileName.at(source_root.length()) == QLatin1Char('/'))) {
+ return build_root + fileName.mid(source_root.length());
+ }
+ return QString();
+}
+
+QString QMakeGlobals::getEnv(const QString &var) const
+{
+#ifdef PROEVALUATOR_SETENV
+ return environment.value(var);
+#else
+ return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
+#endif
+}
+
+QStringList QMakeGlobals::getPathListEnv(const QString &var) const
+{
+ QStringList ret;
+ QString val = getEnv(var);
+ if (!val.isEmpty()) {
+ QDir bdir;
+ QStringList vals = val.split(dirlist_sep);
+ ret.reserve(vals.length());
+ foreach (const QString &it, vals)
+ ret << QDir::cleanPath(bdir.absoluteFilePath(it));
+ }
+ return ret;
+}
+
+QString QMakeGlobals::expandEnvVars(const QString &str) const
+{
+ QString string = str;
+ int rep;
+ QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
+ while ((rep = reg_variableName.indexIn(string)) != -1)
+ string.replace(rep, reg_variableName.matchedLength(),
+ getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
+ return string;
+}
+
+#ifndef QT_BUILD_QMAKE
+#ifdef PROEVALUATOR_INIT_PROPS
+bool QMakeGlobals::initProperties()
+{
+ QByteArray data;
+#ifndef QT_BOOTSTRAPPED
+ QProcess proc;
+ proc.start(qmake_abslocation, QStringList() << QLatin1String("-query"));
+ if (!proc.waitForFinished())
+ return false;
+ data = proc.readAll();
+#else
+ if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake_abslocation) + QLatin1String(" -query"))
+ .toLocal8Bit(), "r")) {
+ char buff[1024];
+ while (!feof(proc))
+ data.append(buff, int(fread(buff, 1, 1023, proc)));
+ QT_PCLOSE(proc);
+ }
+#endif
+ foreach (QByteArray line, data.split('\n'))
+ if (!line.startsWith("QMAKE_")) {
+ int off = line.indexOf(':');
+ if (off < 0) // huh?
+ continue;
+ if (line.endsWith('\r'))
+ line.chop(1);
+ QString name = QString::fromLatin1(line.left(off));
+ ProString value = ProString(QDir::fromNativeSeparators(
+ QString::fromLocal8Bit(line.mid(off + 1))));
+ properties.insert(ProKey(name), value);
+ if (name.startsWith(QLatin1String("QT_")) && !name.contains(QLatin1Char('/'))) {
+ if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
+ properties.insert(ProKey(name + QLatin1String("/raw")), value);
+ properties.insert(ProKey(name + QLatin1String("/get")), value);
+ if (name == QLatin1String("QT_INSTALL_PREFIX")
+ || name == QLatin1String("QT_INSTALL_DATA")
+ || name == QLatin1String("QT_INSTALL_BINS")) {
+ name.replace(3, 7, QLatin1String("HOST"));
+ properties.insert(ProKey(name), value);
+ properties.insert(ProKey(name + QLatin1String("/get")), value);
+ }
+ } else if (name.startsWith(QLatin1String("QT_HOST_"))) {
+ properties.insert(ProKey(name + QLatin1String("/get")), value);
+ }
+ }
+ }
+ properties.insert(ProKey("QMAKE_VERSION"), ProString("2.01a"));
+ return true;
+}
+#else
+void QMakeGlobals::setProperties(const QHash<QString, QString> &props)
+{
+ QHash<QString, QString>::ConstIterator it = props.constBegin(), eit = props.constEnd();
+ for (; it != eit; ++it)
+ properties.insert(ProKey(it.key()), ProString(it.value()));
+}
+#endif
+#endif // QT_BUILD_QMAKE
+
+QT_END_NAMESPACE
diff --git a/qmake/library/qmakeglobals.h b/qmake/library/qmakeglobals.h
new file mode 100644
index 0000000000..a0e07238ce
--- /dev/null
+++ b/qmake/library/qmakeglobals.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEGLOBALS_H
+#define QMAKEGLOBALS_H
+
+#include "qmake_global.h"
+#include "proitems.h"
+
+#ifdef QT_BUILD_QMAKE
+# include <property.h>
+#endif
+
+#include <qhash.h>
+#include <qstringlist.h>
+#ifndef QT_BOOTSTRAPPED
+# include <qprocess.h>
+#endif
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qmutex.h>
+# include <qwaitcondition.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMakeEvaluator;
+
+class QMakeBaseKey
+{
+public:
+ QMakeBaseKey(const QString &_root, bool _hostBuild);
+
+ QString root;
+ bool hostBuild;
+};
+
+uint qHash(const QMakeBaseKey &key);
+bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two);
+
+class QMakeBaseEnv
+{
+public:
+ QMakeBaseEnv();
+ ~QMakeBaseEnv();
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+ QMutex mutex;
+ QWaitCondition cond;
+ bool inProgress;
+ // The coupling of this flag to thread safety exists because for other
+ // use cases failure is immediately fatal anyway.
+ bool isOk;
+#endif
+ QMakeEvaluator *evaluator;
+};
+
+class QMAKE_EXPORT QMakeCmdLineParserState
+{
+public:
+ QMakeCmdLineParserState(const QString &_pwd) : pwd(_pwd), after(false) {}
+ QString pwd;
+ QStringList precmds, preconfigs, postcmds, postconfigs;
+ bool after;
+};
+
+class QMAKE_EXPORT QMakeGlobals
+{
+public:
+ QMakeGlobals();
+ ~QMakeGlobals();
+
+ bool do_cache;
+ QString dir_sep;
+ QString dirlist_sep;
+ QString cachefile;
+#ifdef PROEVALUATOR_SETENV
+ QProcessEnvironment environment;
+#endif
+ QString qmake_abslocation;
+
+ QString qmakespec, xqmakespec;
+ QString user_template, user_template_prefix;
+ QString precmds, postcmds;
+
+#ifdef PROEVALUATOR_DEBUG
+ int debugLevel;
+#endif
+
+ enum ArgumentReturn { ArgumentUnknown, ArgumentMalformed, ArgumentsOk };
+ ArgumentReturn addCommandLineArguments(QMakeCmdLineParserState &state,
+ QStringList &args, int *pos);
+ void commitCommandLineArguments(QMakeCmdLineParserState &state);
+ void setCommandLineArguments(const QString &pwd, const QStringList &args);
+ void useEnvironment();
+ void setDirectories(const QString &input_dir, const QString &output_dir);
+#ifdef QT_BUILD_QMAKE
+ void setQMakeProperty(QMakeProperty *prop) { property = prop; }
+ ProString propertyValue(const ProKey &name) const { return property->value(name); }
+#else
+# ifdef PROEVALUATOR_INIT_PROPS
+ bool initProperties();
+# else
+ void setProperties(const QHash<QString, QString> &props);
+# endif
+ ProString propertyValue(const ProKey &name) const { return properties.value(name); }
+#endif
+
+ QString expandEnvVars(const QString &str) const;
+ QString shadowedPath(const QString &fileName) const;
+
+private:
+ QString getEnv(const QString &) const;
+ QStringList getPathListEnv(const QString &var) const;
+
+ QString cleanSpec(QMakeCmdLineParserState &state, const QString &spec);
+
+ QString source_root, build_root;
+
+#ifdef QT_BUILD_QMAKE
+ QMakeProperty *property;
+#else
+ QHash<ProKey, ProString> properties;
+#endif
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+ QMutex mutex;
+#endif
+ QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;
+
+ friend class QMakeEvaluator;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMAKEGLOBALS_H
diff --git a/qmake/library/qmakeparser.cpp b/qmake/library/qmakeparser.cpp
new file mode 100644
index 0000000000..bf77f77c0f
--- /dev/null
+++ b/qmake/library/qmakeparser.cpp
@@ -0,0 +1,1185 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeparser.h"
+
+#include "ioutils.h"
+using namespace QMakeInternal;
+
+#include <qfile.h>
+#ifdef PROPARSER_THREAD_SAFE
+# include <qthreadpool.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileCache
+//
+///////////////////////////////////////////////////////////////////////
+
+ProFileCache::~ProFileCache()
+{
+ foreach (const Entry &ent, parsed_files)
+ if (ent.pro)
+ ent.pro->deref();
+}
+
+void ProFileCache::discardFile(const QString &fileName)
+{
+#ifdef PROPARSER_THREAD_SAFE
+ QMutexLocker lck(&mutex);
+#endif
+ QHash<QString, Entry>::Iterator it = parsed_files.find(fileName);
+ if (it != parsed_files.end()) {
+ if (it->pro)
+ it->pro->deref();
+ parsed_files.erase(it);
+ }
+}
+
+void ProFileCache::discardFiles(const QString &prefix)
+{
+#ifdef PROPARSER_THREAD_SAFE
+ QMutexLocker lck(&mutex);
+#endif
+ QHash<QString, Entry>::Iterator
+ it = parsed_files.begin(),
+ end = parsed_files.end();
+ while (it != end)
+ if (it.key().startsWith(prefix)) {
+ if (it->pro)
+ it->pro->deref();
+ it = parsed_files.erase(it);
+ } else {
+ ++it;
+ }
+}
+
+
+////////// Parser ///////////
+
+#define fL1S(s) QString::fromLatin1(s)
+
+namespace { // MSVC2010 doesn't seem to know the semantics of "static" ...
+
+static struct {
+ QString strelse;
+ QString strfor;
+ QString strdefineTest;
+ QString strdefineReplace;
+ QString stroption;
+ QString strhost_build;
+ QString strLINE;
+ QString strFILE;
+ QString strLITERAL_HASH;
+ QString strLITERAL_DOLLAR;
+ QString strLITERAL_WHITESPACE;
+} statics;
+
+}
+
+void QMakeParser::initialize()
+{
+ if (!statics.strelse.isNull())
+ return;
+
+ statics.strelse = QLatin1String("else");
+ statics.strfor = QLatin1String("for");
+ statics.strdefineTest = QLatin1String("defineTest");
+ statics.strdefineReplace = QLatin1String("defineReplace");
+ statics.stroption = QLatin1String("option");
+ statics.strhost_build = QLatin1String("host_build");
+ statics.strLINE = QLatin1String("_LINE_");
+ statics.strFILE = QLatin1String("_FILE_");
+ statics.strLITERAL_HASH = QLatin1String("LITERAL_HASH");
+ statics.strLITERAL_DOLLAR = QLatin1String("LITERAL_DOLLAR");
+ statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE");
+}
+
+QMakeParser::QMakeParser(ProFileCache *cache, QMakeParserHandler *handler)
+ : m_cache(cache)
+ , m_handler(handler)
+{
+ // So that single-threaded apps don't have to call initialize() for now.
+ initialize();
+}
+
+ProFile *QMakeParser::parsedProFile(const QString &fileName, bool cache)
+{
+ ProFile *pro;
+ if (cache && m_cache) {
+ ProFileCache::Entry *ent;
+#ifdef PROPARSER_THREAD_SAFE
+ QMutexLocker locker(&m_cache->mutex);
+#endif
+ QHash<QString, ProFileCache::Entry>::Iterator it = m_cache->parsed_files.find(fileName);
+ if (it != m_cache->parsed_files.end()) {
+ ent = &*it;
+#ifdef PROPARSER_THREAD_SAFE
+ if (ent->locker && !ent->locker->done) {
+ ++ent->locker->waiters;
+ QThreadPool::globalInstance()->releaseThread();
+ ent->locker->cond.wait(locker.mutex());
+ QThreadPool::globalInstance()->reserveThread();
+ if (!--ent->locker->waiters) {
+ delete ent->locker;
+ ent->locker = 0;
+ }
+ }
+#endif
+ if ((pro = ent->pro))
+ pro->ref();
+ } else {
+ ent = &m_cache->parsed_files[fileName];
+#ifdef PROPARSER_THREAD_SAFE
+ ent->locker = new ProFileCache::Entry::Locker;
+ locker.unlock();
+#endif
+ pro = new ProFile(fileName);
+ if (!read(pro)) {
+ delete pro;
+ pro = 0;
+ } else {
+ pro->itemsRef()->squeeze();
+ pro->ref();
+ }
+ ent->pro = pro;
+#ifdef PROPARSER_THREAD_SAFE
+ locker.relock();
+ if (ent->locker->waiters) {
+ ent->locker->done = true;
+ ent->locker->cond.wakeAll();
+ } else {
+ delete ent->locker;
+ ent->locker = 0;
+ }
+#endif
+ }
+ } else {
+ pro = new ProFile(fileName);
+ if (!read(pro)) {
+ delete pro;
+ pro = 0;
+ }
+ }
+ return pro;
+}
+
+ProFile *QMakeParser::parsedProBlock(
+ const QString &contents, const QString &name, int line, SubGrammar grammar)
+{
+ ProFile *pro = new ProFile(name);
+ if (!read(pro, contents, line, grammar)) {
+ delete pro;
+ pro = 0;
+ }
+ return pro;
+}
+
+bool QMakeParser::read(ProFile *pro)
+{
+ QFile file(pro->fileName());
+ if (!file.open(QIODevice::ReadOnly)) {
+ if (m_handler && IoUtils::exists(pro->fileName()))
+ m_handler->message(QMakeParserHandler::ParserIoError,
+ fL1S("Cannot read %1: %2").arg(pro->fileName(), file.errorString()));
+ return false;
+ }
+
+ QByteArray bcont = file.readAll();
+ if (bcont.startsWith(QByteArray("\xef\xbb\xbf"))) {
+ // UTF-8 BOM will cause subtle errors
+ m_handler->message(QMakeParserHandler::ParserIoError,
+ fL1S("Unexpected UTF-8 BOM in %1").arg(pro->fileName()));
+ return false;
+ }
+ QString content(QString::fromLocal8Bit(bcont));
+ bcont.clear();
+ file.close();
+ return read(pro, content, 1, FullGrammar);
+}
+
+void QMakeParser::putTok(ushort *&tokPtr, ushort tok)
+{
+ *tokPtr++ = tok;
+}
+
+void QMakeParser::putBlockLen(ushort *&tokPtr, uint len)
+{
+ *tokPtr++ = (ushort)len;
+ *tokPtr++ = (ushort)(len >> 16);
+}
+
+void QMakeParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len)
+{
+ memcpy(tokPtr, buf, len * 2);
+ tokPtr += len;
+}
+
+void QMakeParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
+{
+ uint hash = ProString::hash((const QChar *)buf, len);
+ ushort *tokPtr = pTokPtr;
+ *tokPtr++ = (ushort)hash;
+ *tokPtr++ = (ushort)(hash >> 16);
+ *tokPtr++ = (ushort)len;
+ memcpy(tokPtr, buf, len * 2);
+ pTokPtr = tokPtr + len;
+}
+
+void QMakeParser::finalizeHashStr(ushort *buf, uint len)
+{
+ buf[-4] = TokHashLiteral;
+ buf[-1] = len;
+ uint hash = ProString::hash((const QChar *)buf, len);
+ buf[-3] = (ushort)hash;
+ buf[-2] = (ushort)(hash >> 16);
+}
+
+bool QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar grammar)
+{
+ m_proFile = pro;
+ m_lineNo = line;
+
+ // Final precompiled token stream buffer
+ QString tokBuff;
+ // Worst-case size calculations:
+ // - line marker adds 1 (2-nl) to 1st token of each line
+ // - empty assignment "A=":2 =>
+ // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
+ // TokValueTerminator(1) == 7 (8)
+ // - non-empty assignment "A=B C":5 =>
+ // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
+ // TokLiteral(1) + len(1) + "B"(1) +
+ // TokLiteral(1) + len(1) + "C"(1) + TokValueTerminator(1) == 13 (14)
+ // - variable expansion: "$$f":3 =>
+ // TokVariable(1) + hash(2) + len(1) + "f"(1) = 5
+ // - function expansion: "$$f()":5 =>
+ // TokFuncName(1) + hash(2) + len(1) + "f"(1) + TokFuncTerminator(1) = 6
+ // - scope: "X:":2 =>
+ // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokCondition(1) +
+ // TokBranch(1) + len(2) + ... + len(2) + ... == 10
+ // - test: "X():":4 =>
+ // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokTestCall(1) + TokFuncTerminator(1) +
+ // TokBranch(1) + len(2) + ... + len(2) + ... == 11
+ // - "for(A,B):":9 =>
+ // TokForLoop(1) + hash(2) + len(1) + "A"(1) +
+ // len(2) + TokLiteral(1) + len(1) + "B"(1) + TokValueTerminator(1) +
+ // len(2) + ... + TokTerminator(1) == 14 (15)
+ tokBuff.reserve((in.size() + 1) * 5);
+ ushort *tokPtr = (ushort *)tokBuff.constData(); // Current writing position
+
+ // Expression precompiler buffer.
+ QString xprBuff;
+ xprBuff.reserve(tokBuff.capacity()); // Excessive, but simple
+ ushort *buf = (ushort *)xprBuff.constData();
+
+ // Parser state
+ m_blockstack.clear();
+ m_blockstack.resize(1);
+
+ QStack<ParseCtx> xprStack;
+ xprStack.reserve(10);
+
+ // We rely on QStrings being null-terminated, so don't maintain a global end pointer.
+ const ushort *cur = (const ushort *)in.unicode();
+ m_canElse = false;
+ freshLine:
+ m_state = StNew;
+ m_invert = false;
+ m_operator = NoOperator;
+ m_markLine = m_lineNo;
+ m_inError = false;
+ int parens = 0; // Braces in value context
+ int argc = 0;
+ int wordCount = 0; // Number of words in currently accumulated expression
+ int lastIndent = 0; // Previous line's indentation, to detect accidental continuation abuse
+ bool putSpace = false; // Only ever true inside quoted string
+ bool lineMarked = true; // For in-expression markers
+ ushort needSep = TokNewStr; // Complementary to putSpace: separator outside quotes
+ ushort quote = 0;
+ ushort term = 0;
+
+ Context context;
+ ushort *ptr;
+ if (grammar == ValueGrammar) {
+ context = CtxPureValue;
+ ptr = tokPtr + 2;
+ } else {
+ context = CtxTest;
+ ptr = buf + 4;
+ }
+ ushort *xprPtr = ptr;
+
+#define FLUSH_LHS_LITERAL() \
+ do { \
+ if ((tlen = ptr - xprPtr)) { \
+ finalizeHashStr(xprPtr, tlen); \
+ if (needSep) { \
+ wordCount++; \
+ needSep = 0; \
+ } \
+ } else { \
+ ptr -= 4; \
+ } \
+ } while (0)
+
+#define FLUSH_RHS_LITERAL() \
+ do { \
+ if ((tlen = ptr - xprPtr)) { \
+ xprPtr[-2] = TokLiteral | needSep; \
+ xprPtr[-1] = tlen; \
+ if (needSep) { \
+ wordCount++; \
+ needSep = 0; \
+ } \
+ } else { \
+ ptr -= 2; \
+ } \
+ } while (0)
+
+#define FLUSH_LITERAL() \
+ do { \
+ if (context == CtxTest) \
+ FLUSH_LHS_LITERAL(); \
+ else \
+ FLUSH_RHS_LITERAL(); \
+ } while (0)
+
+#define FLUSH_VALUE_LIST() \
+ do { \
+ if (wordCount > 1) { \
+ xprPtr = tokPtr; \
+ if (*xprPtr == TokLine) \
+ xprPtr += 2; \
+ tokPtr[-1] = ((*xprPtr & TokMask) == TokLiteral) ? wordCount : 0; \
+ } else { \
+ tokPtr[-1] = 0; \
+ } \
+ tokPtr = ptr; \
+ putTok(tokPtr, TokValueTerminator); \
+ } while (0)
+
+ const ushort *end; // End of this line
+ const ushort *cptr; // Start of next line
+ bool lineCont;
+ int indent;
+
+ if (context == CtxPureValue) {
+ end = (const ushort *)in.unicode() + in.length();
+ cptr = 0;
+ lineCont = false;
+ indent = 0; // just gcc being stupid
+ goto nextChr;
+ }
+
+ forever {
+ ushort c;
+
+ // First, skip leading whitespace
+ for (indent = 0; ; ++cur, ++indent) {
+ c = *cur;
+ if (c == '\n') {
+ ++cur;
+ goto flushLine;
+ } else if (!c) {
+ cur = 0;
+ goto flushLine;
+ } else if (c != ' ' && c != '\t' && c != '\r') {
+ break;
+ }
+ }
+
+ // Then strip comments. Yep - no escaping is possible.
+ for (cptr = cur;; ++cptr) {
+ c = *cptr;
+ if (c == '#') {
+ for (end = cptr; (c = *++cptr);) {
+ if (c == '\n') {
+ ++cptr;
+ break;
+ }
+ }
+ if (end == cur) { // Line with only a comment (sans whitespace)
+ if (m_markLine == m_lineNo)
+ m_markLine++;
+ // Qmake bizarreness: such lines do not affect line continuations
+ goto ignore;
+ }
+ break;
+ }
+ if (!c) {
+ end = cptr;
+ break;
+ }
+ if (c == '\n') {
+ end = cptr++;
+ break;
+ }
+ }
+
+ // Then look for line continuations. Yep - no escaping here as well.
+ forever {
+ // We don't have to check for underrun here, as we already determined
+ // that the line is non-empty.
+ ushort ec = *(end - 1);
+ if (ec == '\\') {
+ --end;
+ lineCont = true;
+ break;
+ }
+ if (ec != ' ' && ec != '\t' && ec != '\r') {
+ lineCont = false;
+ break;
+ }
+ --end;
+ }
+
+ // Finally, do the tokenization
+ ushort tok, rtok;
+ int tlen;
+ newWord:
+ do {
+ if (cur == end)
+ goto lineEnd;
+ c = *cur++;
+ } while (c == ' ' || c == '\t');
+ forever {
+ if (c == '$') {
+ if (*cur == '$') { // may be EOF, EOL, WS, '#' or '\\' if past end
+ cur++;
+ if (putSpace) {
+ putSpace = false;
+ *ptr++ = ' ';
+ }
+ FLUSH_LITERAL();
+ if (!lineMarked) {
+ lineMarked = true;
+ *ptr++ = TokLine;
+ *ptr++ = (ushort)m_lineNo;
+ }
+ term = 0;
+ tok = TokVariable;
+ c = *cur;
+ if (c == '[') {
+ ptr += 4;
+ tok = TokProperty;
+ term = ']';
+ c = *++cur;
+ } else if (c == '{') {
+ ptr += 4;
+ term = '}';
+ c = *++cur;
+ } else if (c == '(') {
+ ptr += 2;
+ tok = TokEnvVar;
+ term = ')';
+ c = *++cur;
+ } else {
+ ptr += 4;
+ }
+ xprPtr = ptr;
+ rtok = tok;
+ while ((c & 0xFF00) || c == '.' || c == '_' ||
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || (c == '/' && term)) {
+ *ptr++ = c;
+ if (++cur == end) {
+ c = 0;
+ goto notfunc;
+ }
+ c = *cur;
+ }
+ if (tok == TokVariable && c == '(')
+ tok = TokFuncName;
+ notfunc:
+ if (ptr == xprPtr)
+ languageWarning(fL1S("Missing name in expansion"));
+ if (quote)
+ tok |= TokQuoted;
+ if (needSep) {
+ tok |= needSep;
+ wordCount++;
+ }
+ tlen = ptr - xprPtr;
+ if (rtok != TokVariable
+ || !resolveVariable(xprPtr, tlen, needSep, &ptr,
+ &buf, &xprBuff, &tokPtr, &tokBuff, cur, in)) {
+ if (rtok == TokVariable || rtok == TokProperty) {
+ xprPtr[-4] = tok;
+ uint hash = ProString::hash((const QChar *)xprPtr, tlen);
+ xprPtr[-3] = (ushort)hash;
+ xprPtr[-2] = (ushort)(hash >> 16);
+ xprPtr[-1] = tlen;
+ } else {
+ xprPtr[-2] = tok;
+ xprPtr[-1] = tlen;
+ }
+ }
+ if ((tok & TokMask) == TokFuncName) {
+ cur++;
+ funcCall:
+ {
+ xprStack.resize(xprStack.size() + 1);
+ ParseCtx &top = xprStack.top();
+ top.parens = parens;
+ top.quote = quote;
+ top.terminator = term;
+ top.context = context;
+ top.argc = argc;
+ top.wordCount = wordCount;
+ }
+ parens = 0;
+ quote = 0;
+ term = 0;
+ argc = 1;
+ context = CtxArgs;
+ nextToken:
+ wordCount = 0;
+ nextWord:
+ ptr += (context == CtxTest) ? 4 : 2;
+ xprPtr = ptr;
+ needSep = TokNewStr;
+ goto newWord;
+ }
+ if (term) {
+ checkTerm:
+ if (c != term) {
+ parseError(fL1S("Missing %1 terminator [found %2]")
+ .arg(QChar(term))
+ .arg(c ? QString(c) : QString::fromLatin1("end-of-line")));
+ pro->setOk(false);
+ m_inError = true;
+ // Just parse on, as if there was a terminator ...
+ } else {
+ cur++;
+ }
+ }
+ joinToken:
+ ptr += (context == CtxTest) ? 4 : 2;
+ xprPtr = ptr;
+ needSep = 0;
+ goto nextChr;
+ }
+ } else if (c == '\\') {
+ static const char symbols[] = "[]{}()$\\'\"";
+ ushort c2;
+ if (cur != end && !((c2 = *cur) & 0xff00) && strchr(symbols, c2)) {
+ c = c2;
+ cur++;
+ } else {
+ deprecationWarning(fL1S("Unescaped backslashes are deprecated"));
+ }
+ } else if (quote) {
+ if (c == quote) {
+ quote = 0;
+ if (putSpace) {
+ putSpace = false;
+ *ptr++ = ' ';
+ }
+ goto nextChr;
+ } else if ((c == ' ' || c == '\t') && context != CtxPureValue) {
+ putSpace = true;
+ goto nextChr;
+ } else if (c == '!' && ptr == xprPtr && context == CtxTest) {
+ m_invert ^= true;
+ goto nextChr;
+ }
+ } else if (c == '\'' || c == '"') {
+ quote = c;
+ goto nextChr;
+ } else if (context == CtxArgs) {
+ // Function arg context
+ if (c == ' ' || c == '\t') {
+ FLUSH_RHS_LITERAL();
+ goto nextWord;
+ } else if (c == '(') {
+ ++parens;
+ } else if (c == ')') {
+ if (--parens < 0) {
+ FLUSH_RHS_LITERAL();
+ *ptr++ = TokFuncTerminator;
+ int theargc = argc;
+ {
+ ParseCtx &top = xprStack.top();
+ parens = top.parens;
+ quote = top.quote;
+ term = top.terminator;
+ context = top.context;
+ argc = top.argc;
+ wordCount = top.wordCount;
+ xprStack.resize(xprStack.size() - 1);
+ }
+ if (term == ':') {
+ finalizeCall(tokPtr, buf, ptr, theargc);
+ goto nextItem;
+ } else if (term == '}') {
+ c = (cur == end) ? 0 : *cur;
+ goto checkTerm;
+ } else {
+ Q_ASSERT(!term);
+ goto joinToken;
+ }
+ }
+ } else if (!parens && c == ',') {
+ FLUSH_RHS_LITERAL();
+ *ptr++ = TokArgSeparator;
+ argc++;
+ goto nextToken;
+ }
+ } else if (context == CtxTest) {
+ // Test or LHS context
+ if (c == ' ' || c == '\t') {
+ FLUSH_LHS_LITERAL();
+ goto nextWord;
+ } else if (c == '(') {
+ FLUSH_LHS_LITERAL();
+ if (wordCount != 1) {
+ if (wordCount)
+ parseError(fL1S("Extra characters after test expression."));
+ else
+ parseError(fL1S("Opening parenthesis without prior test name."));
+ pro->setOk(false);
+ ptr = buf; // Put empty function name
+ }
+ *ptr++ = TokTestCall;
+ term = ':';
+ goto funcCall;
+ } else if (c == '!' && ptr == xprPtr) {
+ m_invert ^= true;
+ goto nextChr;
+ } else if (c == ':') {
+ FLUSH_LHS_LITERAL();
+ finalizeCond(tokPtr, buf, ptr, wordCount);
+ if (m_state == StNew)
+ parseError(fL1S("And operator without prior condition."));
+ else
+ m_operator = AndOperator;
+ nextItem:
+ ptr = buf;
+ goto nextToken;
+ } else if (c == '|') {
+ FLUSH_LHS_LITERAL();
+ finalizeCond(tokPtr, buf, ptr, wordCount);
+ if (m_state != StCond)
+ parseError(fL1S("Or operator without prior condition."));
+ else
+ m_operator = OrOperator;
+ goto nextItem;
+ } else if (c == '{') {
+ FLUSH_LHS_LITERAL();
+ finalizeCond(tokPtr, buf, ptr, wordCount);
+ flushCond(tokPtr);
+ ++m_blockstack.top().braceLevel;
+ if (grammar == TestGrammar) {
+ parseError(fL1S("Opening scope not permitted in this context."));
+ pro->setOk(false);
+ }
+ goto nextItem;
+ } else if (c == '}') {
+ FLUSH_LHS_LITERAL();
+ finalizeCond(tokPtr, buf, ptr, wordCount);
+ flushScopes(tokPtr);
+ closeScope:
+ if (!m_blockstack.top().braceLevel) {
+ parseError(fL1S("Excess closing brace."));
+ } else if (!--m_blockstack.top().braceLevel
+ && m_blockstack.count() != 1) {
+ leaveScope(tokPtr);
+ m_state = StNew;
+ m_canElse = false;
+ m_markLine = m_lineNo;
+ }
+ goto nextItem;
+ } else if (c == '+') {
+ tok = TokAppend;
+ goto do2Op;
+ } else if (c == '-') {
+ tok = TokRemove;
+ goto do2Op;
+ } else if (c == '*') {
+ tok = TokAppendUnique;
+ goto do2Op;
+ } else if (c == '~') {
+ tok = TokReplace;
+ do2Op:
+ if (*cur == '=') {
+ cur++;
+ goto doOp;
+ }
+ } else if (c == '=') {
+ tok = TokAssign;
+ doOp:
+ FLUSH_LHS_LITERAL();
+ flushCond(tokPtr);
+ putLineMarker(tokPtr);
+ if (grammar == TestGrammar) {
+ parseError(fL1S("Assignment not permitted in this context."));
+ pro->setOk(false);
+ } else if (wordCount != 1) {
+ parseError(fL1S("Assignment needs exactly one word on the left hand side."));
+ pro->setOk(false);
+ // Put empty variable name.
+ } else {
+ putBlock(tokPtr, buf, ptr - buf);
+ }
+ putTok(tokPtr, tok);
+ context = CtxValue;
+ ptr = ++tokPtr;
+ goto nextToken;
+ }
+ } else if (context == CtxValue) {
+ if (c == ' ' || c == '\t') {
+ FLUSH_RHS_LITERAL();
+ goto nextWord;
+ } else if (c == '{') {
+ ++parens;
+ } else if (c == '}') {
+ if (!parens) {
+ FLUSH_RHS_LITERAL();
+ FLUSH_VALUE_LIST();
+ context = CtxTest;
+ goto closeScope;
+ }
+ --parens;
+ } else if (c == '=') {
+ if (indent < lastIndent)
+ languageWarning(fL1S("Possible accidental line continuation"));
+ }
+ }
+ if (putSpace) {
+ putSpace = false;
+ *ptr++ = ' ';
+ }
+ *ptr++ = c;
+ nextChr:
+ if (cur == end)
+ goto lineEnd;
+ c = *cur++;
+ }
+
+ lineEnd:
+ if (lineCont) {
+ if (quote) {
+ putSpace = true;
+ } else {
+ FLUSH_LITERAL();
+ needSep = TokNewStr;
+ ptr += (context == CtxTest) ? 4 : 2;
+ xprPtr = ptr;
+ }
+ } else {
+ cur = cptr;
+ flushLine:
+ FLUSH_LITERAL();
+ if (quote) {
+ parseError(fL1S("Missing closing %1 quote").arg(QChar(quote)));
+ if (!xprStack.isEmpty()) {
+ context = xprStack.at(0).context;
+ xprStack.clear();
+ }
+ goto flErr;
+ } else if (!xprStack.isEmpty()) {
+ parseError(fL1S("Missing closing parenthesis in function call"));
+ context = xprStack.at(0).context;
+ xprStack.clear();
+ flErr:
+ pro->setOk(false);
+ if (context == CtxValue) {
+ tokPtr[-1] = 0; // sizehint
+ putTok(tokPtr, TokValueTerminator);
+ } else if (context == CtxPureValue) {
+ putTok(tokPtr, TokValueTerminator);
+ } else {
+ bogusTest(tokPtr);
+ }
+ } else if (context == CtxValue) {
+ FLUSH_VALUE_LIST();
+ if (parens)
+ languageWarning(fL1S("Possible braces mismatch"));
+ } else if (context == CtxPureValue) {
+ tokPtr = ptr;
+ putTok(tokPtr, TokValueTerminator);
+ } else {
+ finalizeCond(tokPtr, buf, ptr, wordCount);
+ }
+ if (!cur)
+ break;
+ ++m_lineNo;
+ goto freshLine;
+ }
+
+ lastIndent = indent;
+ lineMarked = false;
+ ignore:
+ cur = cptr;
+ ++m_lineNo;
+ }
+
+ flushScopes(tokPtr);
+ if (m_blockstack.size() > 1) {
+ parseError(fL1S("Missing closing brace(s)."));
+ pro->setOk(false);
+ }
+ while (m_blockstack.size())
+ leaveScope(tokPtr);
+ tokBuff.resize(tokPtr - (ushort *)tokBuff.constData()); // Reserved capacity stays
+ *pro->itemsRef() = tokBuff;
+ return true;
+
+#undef FLUSH_VALUE_LIST
+#undef FLUSH_LITERAL
+#undef FLUSH_LHS_LITERAL
+#undef FLUSH_RHS_LITERAL
+}
+
+void QMakeParser::putLineMarker(ushort *&tokPtr)
+{
+ if (m_markLine) {
+ *tokPtr++ = TokLine;
+ *tokPtr++ = (ushort)m_markLine;
+ m_markLine = 0;
+ }
+}
+
+void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
+{
+ m_blockstack.resize(m_blockstack.size() + 1);
+ m_blockstack.top().special = special;
+ m_blockstack.top().start = tokPtr;
+ tokPtr += 2;
+ m_state = state;
+ m_canElse = false;
+ if (special)
+ m_markLine = m_lineNo;
+}
+
+void QMakeParser::leaveScope(ushort *&tokPtr)
+{
+ if (m_blockstack.top().inBranch) {
+ // Put empty else block
+ putBlockLen(tokPtr, 0);
+ }
+ if (ushort *start = m_blockstack.top().start) {
+ putTok(tokPtr, TokTerminator);
+ uint len = tokPtr - start - 2;
+ start[0] = (ushort)len;
+ start[1] = (ushort)(len >> 16);
+ }
+ m_blockstack.resize(m_blockstack.size() - 1);
+}
+
+// If we are on a fresh line, close all open one-line scopes.
+void QMakeParser::flushScopes(ushort *&tokPtr)
+{
+ if (m_state == StNew) {
+ while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1)
+ leaveScope(tokPtr);
+ if (m_blockstack.top().inBranch) {
+ m_blockstack.top().inBranch = false;
+ // Put empty else block
+ putBlockLen(tokPtr, 0);
+ }
+ m_canElse = false;
+ }
+}
+
+// If there is a pending conditional, enter a new scope, otherwise flush scopes.
+void QMakeParser::flushCond(ushort *&tokPtr)
+{
+ if (m_state == StCond) {
+ putTok(tokPtr, TokBranch);
+ m_blockstack.top().inBranch = true;
+ enterScope(tokPtr, false, StNew);
+ } else {
+ flushScopes(tokPtr);
+ }
+}
+
+void QMakeParser::finalizeTest(ushort *&tokPtr)
+{
+ flushScopes(tokPtr);
+ putLineMarker(tokPtr);
+ if (m_operator != NoOperator) {
+ putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
+ m_operator = NoOperator;
+ }
+ if (m_invert) {
+ putTok(tokPtr, TokNot);
+ m_invert = false;
+ }
+ m_state = StCond;
+ m_canElse = true;
+}
+
+void QMakeParser::bogusTest(ushort *&tokPtr)
+{
+ flushScopes(tokPtr);
+ m_operator = NoOperator;
+ m_invert = false;
+ m_state = StCond;
+ m_canElse = true;
+ m_proFile->setOk(false);
+}
+
+void QMakeParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount)
+{
+ if (wordCount != 1) {
+ if (wordCount) {
+ parseError(fL1S("Extra characters after test expression."));
+ bogusTest(tokPtr);
+ }
+ return;
+ }
+
+ // Check for magic tokens
+ if (*uc == TokHashLiteral) {
+ uint nlen = uc[3];
+ ushort *uce = uc + 4 + nlen;
+ if (uce == ptr) {
+ m_tmp.setRawData((QChar *)uc + 4, nlen);
+ if (!m_tmp.compare(statics.strelse, Qt::CaseInsensitive)) {
+ if (m_invert || m_operator != NoOperator) {
+ parseError(fL1S("Unexpected operator in front of else."));
+ return;
+ }
+ BlockScope &top = m_blockstack.top();
+ if (m_canElse && (!top.special || top.braceLevel)) {
+ // A list of tests (the last one likely with side effects),
+ // but no assignment, scope, etc.
+ putTok(tokPtr, TokBranch);
+ // Put empty then block
+ putBlockLen(tokPtr, 0);
+ enterScope(tokPtr, false, StCtrl);
+ return;
+ }
+ forever {
+ BlockScope &top = m_blockstack.top();
+ if (top.inBranch && (!top.special || top.braceLevel)) {
+ top.inBranch = false;
+ enterScope(tokPtr, false, StCtrl);
+ return;
+ }
+ if (top.braceLevel || m_blockstack.size() == 1)
+ break;
+ leaveScope(tokPtr);
+ }
+ parseError(fL1S("Unexpected 'else'."));
+ return;
+ }
+ }
+ }
+
+ finalizeTest(tokPtr);
+ putBlock(tokPtr, uc, ptr - uc);
+ putTok(tokPtr, TokCondition);
+}
+
+void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc)
+{
+ // Check for magic tokens
+ if (*uc == TokHashLiteral) {
+ uint nlen = uc[3];
+ ushort *uce = uc + 4 + nlen;
+ if (*uce == TokTestCall) {
+ uce++;
+ m_tmp.setRawData((QChar *)uc + 4, nlen);
+ const QString *defName;
+ ushort defType;
+ if (m_tmp == statics.strfor) {
+ flushCond(tokPtr);
+ putLineMarker(tokPtr);
+ if (m_invert || m_operator == OrOperator) {
+ // '|' could actually work reasonably, but qmake does nonsense here.
+ parseError(fL1S("Unexpected operator in front of for()."));
+ return;
+ }
+ if (*uce == (TokLiteral|TokNewStr)) {
+ nlen = uce[1];
+ uc = uce + 2 + nlen;
+ if (*uc == TokFuncTerminator) {
+ // for(literal) (only "ever" would be legal if qmake was sane)
+ putTok(tokPtr, TokForLoop);
+ putHashStr(tokPtr, (ushort *)0, (uint)0);
+ putBlockLen(tokPtr, 1 + 3 + nlen + 1);
+ putTok(tokPtr, TokHashLiteral);
+ putHashStr(tokPtr, uce + 2, nlen);
+ didFor:
+ putTok(tokPtr, TokValueTerminator);
+ enterScope(tokPtr, true, StCtrl);
+ return;
+ } else if (*uc == TokArgSeparator && argc == 2) {
+ // for(var, something)
+ uc++;
+ putTok(tokPtr, TokForLoop);
+ putHashStr(tokPtr, uce + 2, nlen);
+ doFor:
+ nlen = ptr - uc;
+ putBlockLen(tokPtr, nlen + 1);
+ putBlock(tokPtr, uc, nlen);
+ goto didFor;
+ }
+ } else if (argc == 1) {
+ // for(non-literal) (this wouldn't be here if qmake was sane)
+ putTok(tokPtr, TokForLoop);
+ putHashStr(tokPtr, (ushort *)0, (uint)0);
+ uc = uce;
+ goto doFor;
+ }
+ parseError(fL1S("Syntax is for(var, list), for(var, forever) or for(ever)."));
+ return;
+ } else if (m_tmp == statics.strdefineReplace) {
+ defName = &statics.strdefineReplace;
+ defType = TokReplaceDef;
+ goto deffunc;
+ } else if (m_tmp == statics.strdefineTest) {
+ defName = &statics.strdefineTest;
+ defType = TokTestDef;
+ deffunc:
+ flushScopes(tokPtr);
+ putLineMarker(tokPtr);
+ if (m_invert) {
+ parseError(fL1S("Unexpected operator in front of function definition."));
+ return;
+ }
+ if (*uce == (TokLiteral|TokNewStr)) {
+ uint nlen = uce[1];
+ if (uce[nlen + 2] == TokFuncTerminator) {
+ if (m_operator != NoOperator) {
+ putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
+ m_operator = NoOperator;
+ }
+ putTok(tokPtr, defType);
+ putHashStr(tokPtr, uce + 2, nlen);
+ enterScope(tokPtr, true, StCtrl);
+ return;
+ }
+ }
+ parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
+ return;
+ } else if (m_tmp == statics.stroption) {
+ if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1
+ || m_invert || m_operator != NoOperator) {
+ parseError(fL1S("option() must appear outside any control structures."));
+ return;
+ }
+ if (*uce == (TokLiteral|TokNewStr)) {
+ uint nlen = uce[1];
+ if (uce[nlen + 2] == TokFuncTerminator) {
+ m_tmp.setRawData((QChar *)uce + 2, nlen);
+ if (m_tmp == statics.strhost_build) {
+ m_proFile->setHostBuild(true);
+ } else {
+ parseError(fL1S("Unknown option() %1.").arg(m_tmp));
+ }
+ return;
+ }
+ }
+ parseError(fL1S("option() requires one literal argument."));
+ return;
+ }
+ }
+ }
+
+ finalizeTest(tokPtr);
+ putBlock(tokPtr, uc, ptr - uc);
+}
+
+bool QMakeParser::resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
+ ushort **buf, QString *xprBuff,
+ ushort **tokPtr, QString *tokBuff,
+ const ushort *cur, const QString &in)
+{
+ QString out;
+ m_tmp.setRawData((const QChar *)xprPtr, tlen);
+ if (m_tmp == statics.strLINE) {
+ out.setNum(m_lineNo);
+ } else if (m_tmp == statics.strFILE) {
+ out = m_proFile->fileName();
+ // The string is typically longer than the variable reference, so we need
+ // to ensure that there is enough space in the output buffer - as unlikely
+ // as an overflow is to actually happen in practice.
+ int need = (in.length() - (cur - (const ushort *)in.constData()) + 2) * 5 + out.length();
+ int tused = *tokPtr - (ushort *)tokBuff->constData();
+ int xused;
+ int total;
+ bool ptrFinal = xprPtr >= (ushort *)tokBuff->constData()
+ && xprPtr < (ushort *)tokBuff->constData() + tokBuff->capacity();
+ if (ptrFinal) {
+ xused = xprPtr - (ushort *)tokBuff->constData();
+ total = xused + need;
+ } else {
+ xused = xprPtr - *buf;
+ total = tused + xused + need;
+ }
+ if (tokBuff->capacity() < total) {
+ tokBuff->reserve(total);
+ *tokPtr = (ushort *)tokBuff->constData() + tused;
+ xprBuff->reserve(total);
+ *buf = (ushort *)xprBuff->constData();
+ xprPtr = (ptrFinal ? (ushort *)tokBuff->constData() : *buf) + xused;
+ }
+ } else if (m_tmp == statics.strLITERAL_HASH) {
+ out = QLatin1String("#");
+ } else if (m_tmp == statics.strLITERAL_DOLLAR) {
+ out = QLatin1String("$");
+ } else if (m_tmp == statics.strLITERAL_WHITESPACE) {
+ out = QLatin1String("\t");
+ } else {
+ return false;
+ }
+ xprPtr -= 2; // Was set up for variable reference
+ xprPtr[-2] = TokLiteral | needSep;
+ xprPtr[-1] = out.length();
+ memcpy(xprPtr, out.constData(), out.length() * 2);
+ *ptr = xprPtr + out.length();
+ return true;
+}
+
+void QMakeParser::message(int type, const QString &msg) const
+{
+ if (!m_inError && m_handler)
+ m_handler->message(type, msg, m_proFile->fileName(), m_lineNo);
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/library/qmakeparser.h b/qmake/library/qmakeparser.h
new file mode 100644
index 0000000000..cbda916dc2
--- /dev/null
+++ b/qmake/library/qmakeparser.h
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEPARSER_H
+#define QMAKEPARSER_H
+
+#include "qmake_global.h"
+#include "proitems.h"
+
+#include <qhash.h>
+#include <qstack.h>
+#ifdef PROPARSER_THREAD_SAFE
+# include <qmutex.h>
+# include <qwaitcondition.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+class QMAKE_EXPORT QMakeParserHandler
+{
+public:
+ enum {
+ CategoryMask = 0xf00,
+ WarningMessage = 0x000,
+ ErrorMessage = 0x100,
+
+ SourceMask = 0xf0,
+ SourceParser = 0,
+
+ CodeMask = 0xf,
+ WarnLanguage = 0,
+ WarnDeprecated,
+
+ ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage,
+ ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated,
+
+ ParserIoError = ErrorMessage | SourceParser,
+ ParserError
+ };
+ virtual void message(int type, const QString &msg,
+ const QString &fileName = QString(), int lineNo = 0) = 0;
+};
+
+class ProFileCache;
+
+class QMAKE_EXPORT QMakeParser
+{
+public:
+ // Call this from a concurrency-free context
+ static void initialize();
+
+ QMakeParser(ProFileCache *cache, QMakeParserHandler *handler);
+
+ enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
+ // fileName is expected to be absolute and cleanPath()ed.
+ ProFile *parsedProFile(const QString &fileName, bool cache = false);
+ ProFile *parsedProBlock(const QString &contents, const QString &name, int line = 0,
+ SubGrammar grammar = FullGrammar);
+
+private:
+ struct BlockScope {
+ BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {}
+ BlockScope(const BlockScope &other) { *this = other; }
+ ushort *start; // Where this block started; store length here
+ int braceLevel; // Nesting of braces in scope
+ bool special; // Single-line conditionals inside loops, etc. cannot have else branches
+ bool inBranch; // The 'else' branch of the previous TokBranch is still open
+ };
+
+ enum ScopeState {
+ StNew, // Fresh scope
+ StCtrl, // Control statement (for or else) met on current line
+ StCond // Conditionals met on current line
+ };
+
+ enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs };
+ struct ParseCtx {
+ int parens; // Nesting of non-functional parentheses
+ int argc; // Number of arguments in current function call
+ int wordCount; // Number of words in current expression
+ Context context;
+ ushort quote; // Enclosing quote type
+ ushort terminator; // '}' if replace function call is braced, ':' if test function
+ };
+
+ bool read(ProFile *pro);
+ bool read(ProFile *pro, const QString &content, int line, SubGrammar grammar);
+
+ ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
+ ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
+ ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len);
+ void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
+ void finalizeHashStr(ushort *buf, uint len);
+ void putLineMarker(ushort *&tokPtr);
+ ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
+ ushort **buf, QString *xprBuff,
+ ushort **tokPtr, QString *tokBuff,
+ const ushort *cur, const QString &in);
+ void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
+ void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
+ void finalizeTest(ushort *&tokPtr);
+ void bogusTest(ushort *&tokPtr);
+ void enterScope(ushort *&tokPtr, bool special, ScopeState state);
+ void leaveScope(ushort *&tokPtr);
+ void flushCond(ushort *&tokPtr);
+ void flushScopes(ushort *&tokPtr);
+
+ void message(int type, const QString &msg) const;
+ void parseError(const QString &msg) const
+ { message(QMakeParserHandler::ParserError, msg); }
+ void languageWarning(const QString &msg) const
+ { message(QMakeParserHandler::ParserWarnLanguage, msg); }
+ void deprecationWarning(const QString &msg) const
+ { message(QMakeParserHandler::ParserWarnDeprecated, msg); }
+
+ // Current location
+ ProFile *m_proFile;
+ int m_lineNo;
+
+ QStack<BlockScope> m_blockstack;
+ ScopeState m_state;
+ int m_markLine; // Put marker for this line
+ bool m_inError; // Current line had a parsing error; suppress followup error messages
+ bool m_canElse; // Conditionals met on previous line, but no scope was opened
+ bool m_invert; // Pending conditional is negated
+ enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed
+
+ QString m_tmp; // Temporary for efficient toQString
+
+ ProFileCache *m_cache;
+ QMakeParserHandler *m_handler;
+
+ // This doesn't help gcc 3.3 ...
+ template<typename T> friend class QTypeInfo;
+
+ friend class ProFileCache;
+};
+
+class QMAKE_EXPORT ProFileCache
+{
+public:
+ ProFileCache() {}
+ ~ProFileCache();
+
+ void discardFile(const QString &fileName);
+ void discardFiles(const QString &prefix);
+
+private:
+ struct Entry {
+ ProFile *pro;
+#ifdef PROPARSER_THREAD_SAFE
+ struct Locker {
+ Locker() : waiters(0), done(false) {}
+ QWaitCondition cond;
+ int waiters;
+ bool done;
+ };
+ Locker *locker;
+#endif
+ };
+
+ QHash<QString, Entry> parsed_files;
+#ifdef PROPARSER_THREAD_SAFE
+ QMutex mutex;
+#endif
+
+ friend class QMakeParser;
+};
+
+#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
+Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // PROFILEPARSER_H
diff --git a/qmake/main.cpp b/qmake/main.cpp
index 91c8146c08..59e6f6794d 100644
--- a/qmake/main.cpp
+++ b/qmake/main.cpp
@@ -85,6 +85,9 @@ int runQMake(int argc, char **argv)
// This is particularly important for things like QtCreator and scripted builds.
setvbuf(stdout, (char *)NULL, _IONBF, 0);
+ QMakeGlobals globals;
+ Option::globals = &globals;
+
// parse command line
int ret = Option::init(argc, argv);
if(ret != Option::QMAKE_CMDLINE_SUCCESS) {
@@ -125,8 +128,14 @@ int runQMake(int argc, char **argv)
Option::qmake_mode == Option::QMAKE_SET_PROPERTY ||
Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY)
return prop.exec() ? 0 : 101;
+ globals.setQMakeProperty(&prop);
+
+ ProFileCache proFileCache;
+ Option::proFileCache = &proFileCache;
+ QMakeParser parser(&proFileCache, &Option::evalHandler);
+ Option::parser = &parser;
- QMakeProject project(&prop);
+ QMakeProject project;
int exit_val = 0;
QStringList files;
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
diff --git a/qmake/meta.cpp b/qmake/meta.cpp
index 93e7ed14a5..49dcdc03b5 100644
--- a/qmake/meta.cpp
+++ b/qmake/meta.cpp
@@ -76,7 +76,7 @@ QMakeMetaInfo::readLib(QString lib)
meta_type = "libtool";
} else if(meta_file.endsWith(Option::prl_ext)) {
QMakeProject proj;
- if(!proj.read(Option::normalizePath(meta_file), QMakeProject::ReadProFile))
+ if (!proj.read(Option::normalizePath(meta_file), QMakeEvaluator::LoadProOnly))
return false;
meta_type = "qmake";
vars = proj.variables();
@@ -136,7 +136,7 @@ QMakeMetaInfo::readLibtoolFile(const QString &f)
/* I can just run the .la through the .pro parser since they are compatible.. */
QMakeProject proj;
QString nf = Option::normalizePath(f);
- if(!proj.read(nf, QMakeProject::ReadProFile))
+ if (!proj.read(nf, QMakeEvaluator::LoadProOnly))
return false;
QString dirf = nf.section(QLatin1Char('/'), 0, -2);
if(dirf == nf)
diff --git a/qmake/option.cpp b/qmake/option.cpp
index 570b54de43..7bb37946c6 100644
--- a/qmake/option.cpp
+++ b/qmake/option.cpp
@@ -51,6 +51,11 @@
QT_BEGIN_NAMESPACE
+EvalHandler Option::evalHandler;
+QMakeGlobals *Option::globals;
+ProFileCache *Option::proFileCache;
+QMakeParser *Option::parser;
+
//convenience
const char *Option::application_argv0 = 0;
QString Option::prf_ext;
@@ -67,7 +72,6 @@ QString Option::lex_ext;
QString Option::yacc_ext;
QString Option::pro_ext;
QString Option::dir_sep;
-QString Option::dirlist_sep;
QString Option::h_moc_mod;
QString Option::yacc_mod;
QString Option::lex_mod;
@@ -78,17 +82,12 @@ char Option::field_sep;
Option::QMAKE_MODE Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING;
//all modes
-QString Option::qmake_abslocation;
QStringList Option::qmake_args;
int Option::warn_level = WarnLogic | WarnDeprecated;
int Option::debug_level = 0;
QFile Option::output;
QString Option::output_dir;
bool Option::recursive = false;
-QStringList Option::before_user_vars;
-QStringList Option::after_user_vars;
-QString Option::user_template;
-QString Option::user_template_prefix;
//QMAKE_*_PROPERTY stuff
QStringList Option::prop::properties;
@@ -98,18 +97,12 @@ bool Option::projfile::do_pwd = true;
QStringList Option::projfile::project_dirs;
//QMAKE_GENERATE_MAKEFILE stuff
-QString Option::mkfile::qmakespec;
-QString Option::mkfile::xqmakespec;
int Option::mkfile::cachefile_depth = -1;
bool Option::mkfile::do_deps = true;
bool Option::mkfile::do_mocs = true;
bool Option::mkfile::do_dep_heuristics = true;
bool Option::mkfile::do_preprocess = false;
bool Option::mkfile::do_stub_makefile = false;
-bool Option::mkfile::do_cache = true;
-QString Option::mkfile::source_root;
-QString Option::mkfile::build_root;
-QString Option::mkfile::cachefile;
QStringList Option::mkfile::project_files;
static Option::QMAKE_MODE default_mode(QString progname)
@@ -138,17 +131,6 @@ static QString detectProjectFile(const QString &path)
return ret;
}
-static QString cleanSpec(const QString &spec)
-{
- QString ret = QDir::cleanPath(spec);
- if (ret.contains('/')) {
- const QFileInfo specDirInfo(ret);
- if (specDirInfo.exists() && specDirInfo.isDir())
- ret = QDir::cleanPath(specDirInfo.absoluteFilePath());
- }
- return ret;
-}
-
QString project_builtin_regx();
bool usage(const char *a0)
{
@@ -213,102 +195,87 @@ bool usage(const char *a0)
int
Option::parseCommandLine(QStringList &args)
{
- QStringList user_configs;
-
- bool before = true;
- args << QString(); // Avoid bounds checking for options which take an argument
- for (int x = 0; x < args.size() - 1; ) {
- QString arg = args.at(x);
- if (arg.size() > 1 && arg.startsWith('-')) { /* options */
- QString opt = arg.mid(1);
- if(opt == "o" || opt == "output") {
- Option::output.setFileName(args.at(x + 1));
- args.erase(args.begin() + x, args.begin() + x + 2);
- continue;
- } else if(opt == "after") {
- before = false;
- } else if(opt == "t" || opt == "template") {
- Option::user_template = args.at(++x);
- } else if(opt == "tp" || opt == "template_prefix") {
- Option::user_template_prefix = args.at(++x);
- } else if(opt == "unix") {
- Option::dir_sep = "/";
- } else if(opt == "win32") {
- Option::dir_sep = "\\";
- } else if(opt == "d") {
- Option::debug_level++;
- } else if(opt == "version" || opt == "v" || opt == "-version") {
- fprintf(stdout,
- "QMake version %s\n"
- "Using Qt version %s in %s\n",
- qmake_version(), QT_VERSION_STR,
- QLibraryInfo::location(QLibraryInfo::LibrariesPath).toLatin1().constData());
+ QMakeCmdLineParserState state(QDir::currentPath());
+ enum { ArgNone, ArgOutput } argState = ArgNone;
+ int x = 0;
+ while (x < args.count()) {
+ switch (argState) {
+ case ArgOutput:
+ Option::output.setFileName(args.at(x--));
+ args.erase(args.begin() + x, args.begin() + x + 2);
+ argState = ArgNone;
+ continue;
+ default:
+ QMakeGlobals::ArgumentReturn cmdRet = globals->addCommandLineArguments(state, args, &x);
+ if (cmdRet == QMakeGlobals::ArgumentsOk)
+ break;
+ if (cmdRet == QMakeGlobals::ArgumentMalformed) {
+ fprintf(stderr, "***Option %s requires a parameter\n", qPrintable(args.at(x - 1)));
+ return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
+ }
+ Q_ASSERT(cmdRet == QMakeGlobals::ArgumentUnknown);
+ QString arg = args.at(x);
+ if (arg.startsWith(QLatin1Char('-'))) {
+ if (arg == "-d") {
+ Option::debug_level++;
+ } else if (arg == "-v" || arg == "-version" || arg == "--version") {
+ fprintf(stdout,
+ "QMake version %s\n"
+ "Using Qt version %s in %s\n",
+ qmake_version(), QT_VERSION_STR,
+ QLibraryInfo::location(QLibraryInfo::LibrariesPath).toLatin1().constData());
#ifdef QMAKE_OPENSOURCE_VERSION
- fprintf(stdout, "QMake is Open Source software from Nokia Corporation and/or its subsidiary(-ies).\n");
+ fprintf(stdout, "QMake is Open Source software from Nokia Corporation and/or its subsidiary(-ies).\n");
#endif
- return Option::QMAKE_CMDLINE_BAIL;
- } else if(opt == "h" || opt == "help") {
- return Option::QMAKE_CMDLINE_SHOW_USAGE;
- } else if(opt == "Wall") {
- Option::warn_level |= WarnAll;
- } else if(opt == "Wparser") {
- Option::warn_level |= WarnParser;
- } else if(opt == "Wlogic") {
- Option::warn_level |= WarnLogic;
- } else if(opt == "Wdeprecated") {
- Option::warn_level |= WarnDeprecated;
- } else if(opt == "Wnone") {
- Option::warn_level = WarnNone;
- } else if(opt == "r" || opt == "recursive") {
- Option::recursive = true;
- args.removeAt(x);
- continue;
- } else if(opt == "nr" || opt == "norecursive") {
- Option::recursive = false;
- args.removeAt(x);
- continue;
- } else if(opt == "config") {
- user_configs += args.at(++x);
- } else {
- if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
- Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
- if(opt == "nodepend" || opt == "nodepends") {
- Option::mkfile::do_deps = false;
- } else if(opt == "nomoc") {
- Option::mkfile::do_mocs = false;
- } else if(opt == "nocache") {
- Option::mkfile::do_cache = false;
- } else if(opt == "createstub") {
- Option::mkfile::do_stub_makefile = true;
- } else if(opt == "nodependheuristics") {
- Option::mkfile::do_dep_heuristics = false;
- } else if(opt == "E") {
- Option::mkfile::do_preprocess = true;
- } else if(opt == "cache") {
- Option::mkfile::cachefile = args.at(++x);
- } else if(opt == "platform" || opt == "spec") {
- Option::mkfile::qmakespec = args[x] = cleanSpec(args.at(++x));
- } else if (opt == "xplatform" || opt == "xspec") {
- Option::mkfile::xqmakespec = args[x] = cleanSpec(args.at(x));
- } else {
- fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData());
- return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
- }
- } else if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
- if(opt == "nopwd") {
- Option::projfile::do_pwd = false;
- } else {
- fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData());
- return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
+ return Option::QMAKE_CMDLINE_BAIL;
+ } else if (arg == "-h" || arg == "-help" || arg == "--help") {
+ return Option::QMAKE_CMDLINE_SHOW_USAGE;
+ } else if (arg == "-Wall") {
+ Option::warn_level |= WarnAll;
+ } else if (arg == "-Wparser") {
+ Option::warn_level |= WarnParser;
+ } else if (arg == "-Wlogic") {
+ Option::warn_level |= WarnLogic;
+ } else if (arg == "-Wdeprecated") {
+ Option::warn_level |= WarnDeprecated;
+ } else if (arg == "-Wnone") {
+ Option::warn_level = WarnNone;
+ } else if (arg == "-r" || arg == "-recursive") {
+ Option::recursive = true;
+ args.removeAt(x);
+ continue;
+ } else if (arg == "-nr" || arg == "-norecursive") {
+ Option::recursive = false;
+ args.removeAt(x);
+ continue;
+ } else if (arg == "-o" || arg == "-output") {
+ argState = ArgOutput;
+ } else {
+ if (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
+ Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
+ if (arg == "-nodepend" || arg == "-nodepends") {
+ Option::mkfile::do_deps = false;
+ } else if (arg == "-nomoc") {
+ Option::mkfile::do_mocs = false;
+ } else if (arg == "-createstub") {
+ Option::mkfile::do_stub_makefile = true;
+ } else if (arg == "-nodependheuristics") {
+ Option::mkfile::do_dep_heuristics = false;
+ } else if (arg == "-E") {
+ Option::mkfile::do_preprocess = true;
+ } else {
+ fprintf(stderr, "***Unknown option %s\n", arg.toLatin1().constData());
+ return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
+ }
+ } else if (Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
+ if (arg == "-nopwd") {
+ Option::projfile::do_pwd = false;
+ } else {
+ fprintf(stderr, "***Unknown option %s\n", arg.toLatin1().constData());
+ return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
+ }
}
}
- }
- } else {
- if(arg.indexOf('=') != -1) {
- if(before)
- Option::before_user_vars.append(arg);
- else
- Option::after_user_vars.append(arg);
} else {
bool handled = true;
if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY ||
@@ -342,14 +309,12 @@ Option::parseCommandLine(QStringList &args)
}
x++;
}
-
- if (!user_configs.isEmpty())
- Option::before_user_vars += "CONFIG += " + user_configs.join(" ");
-
- if (Option::mkfile::xqmakespec.isEmpty())
- Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
-
- args.takeLast();
+ if (argState != ArgNone) {
+ fprintf(stderr, "***Option %s requires a parameter\n", qPrintable(args.at(x - 1)));
+ return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
+ }
+ globals->commitCommandLineArguments(state);
+ globals->debugLevel = Option::debug_level;
return Option::QMAKE_CMDLINE_SUCCESS;
}
@@ -359,13 +324,6 @@ Option::init(int argc, char **argv)
Option::application_argv0 = 0;
Option::prf_ext = ".prf";
Option::pro_ext = ".pro";
-#ifdef Q_OS_WIN
- Option::dir_sep = "\\";
- Option::dirlist_sep = ";";
-#else
- Option::dir_sep = "/";
- Option::dirlist_sep = ":";
-#endif
Option::field_sep = ' ';
if(argc && argv) {
@@ -374,13 +332,13 @@ Option::init(int argc, char **argv)
if(Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
Option::qmake_mode = default_mode(argv0);
if(!argv0.isEmpty() && !QFileInfo(argv0).isRelative()) {
- Option::qmake_abslocation = argv0;
+ globals->qmake_abslocation = argv0;
} else if (argv0.contains(QLatin1Char('/'))
#ifdef Q_OS_WIN
|| argv0.contains(QLatin1Char('\\'))
#endif
) { //relative PWD
- Option::qmake_abslocation = QDir::current().absoluteFilePath(argv0);
+ globals->qmake_abslocation = QDir::current().absoluteFilePath(argv0);
} else { //in the PATH
QByteArray pEnv = qgetenv("PATH");
QDir currentDir = QDir::current();
@@ -397,16 +355,16 @@ Option::init(int argc, char **argv)
candidate += ".exe";
#endif
if (QFile::exists(candidate)) {
- Option::qmake_abslocation = candidate;
+ globals->qmake_abslocation = candidate;
break;
}
}
}
- if(!Option::qmake_abslocation.isNull())
- Option::qmake_abslocation = QDir::cleanPath(Option::qmake_abslocation);
+ if (!globals->qmake_abslocation.isNull())
+ globals->qmake_abslocation = QDir::cleanPath(globals->qmake_abslocation);
else // This is rather unlikely to ever happen on a modern system ...
- Option::qmake_abslocation = QLibraryInfo::rawLocation(QLibraryInfo::HostBinariesPath,
- QLibraryInfo::EffectivePaths) +
+ globals->qmake_abslocation = QLibraryInfo::rawLocation(QLibraryInfo::HostBinariesPath,
+ QLibraryInfo::EffectivePaths) +
#ifdef Q_OS_WIN
"/qmake.exe";
#else
@@ -485,13 +443,7 @@ Option::init(int argc, char **argv)
//last chance for defaults
if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
- if (Option::mkfile::xqmakespec.isEmpty())
- Option::mkfile::xqmakespec = QString::fromLocal8Bit(qgetenv("XQMAKESPEC").constData());
- if (Option::mkfile::qmakespec.isEmpty()) {
- Option::mkfile::qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").constData());
- if (Option::mkfile::xqmakespec.isEmpty())
- Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
- }
+ globals->useEnvironment();
//try REALLY hard to do it for them, lazy..
if(Option::mkfile::project_files.isEmpty()) {
@@ -513,23 +465,7 @@ Option::init(int argc, char **argv)
void Option::prepareProject(const QString &pfile)
{
QString srcpath = QDir::cleanPath(QFileInfo(pfile).absolutePath());
- if (srcpath != output_dir) {
- if (!srcpath.endsWith(QLatin1Char('/')))
- srcpath += QLatin1Char('/');
- QString dstpath = output_dir;
- if (!dstpath.endsWith(QLatin1Char('/')))
- dstpath += QLatin1Char('/');
- int srcLen = srcpath.length();
- int dstLen = dstpath.length();
- int lastSl = -1;
- while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen))
- if (srcpath.at(srcLen) == QLatin1Char('/'))
- lastSl = 0;
- mkfile::source_root = srcpath.left(srcLen + lastSl);
- mkfile::build_root = dstpath.left(dstLen + lastSl);
- } else {
- mkfile::source_root.clear();
- }
+ globals->setDirectories(srcpath, output_dir);
}
bool Option::postProcessProject(QMakeProject *project)
@@ -550,6 +486,8 @@ bool Option::postProcessProject(QMakeProject *project)
Option::lex_mod = project->first("QMAKE_MOD_LEX").toQString();
Option::yacc_mod = project->first("QMAKE_MOD_YACC").toQString();
+ Option::dir_sep = project->dirSep().toQString();
+
if (Option::output_dir.startsWith(project->buildRoot()))
Option::mkfile::cachefile_depth =
Option::output_dir.mid(project->buildRoot().length()).count('/');
@@ -665,6 +603,37 @@ void warn_msg(QMakeWarn type, const char *fmt, ...)
fprintf(stderr, "\n");
}
+void EvalHandler::message(int type, const QString &msg, const QString &fileName, int lineNo)
+{
+ QString pfx;
+ if ((type & QMakeHandler::CategoryMask) == QMakeHandler::WarningMessage) {
+ int code = (type & QMakeHandler::CodeMask);
+ if ((code == QMakeHandler::WarnLanguage && !(Option::warn_level & WarnParser))
+ || (code == QMakeHandler::WarnDeprecated && !(Option::warn_level & WarnDeprecated)))
+ return;
+ pfx = QString::fromLatin1("WARNING: ");
+ }
+ if (lineNo > 0)
+ fprintf(stderr, "%s%s:%d: %s\n", qPrintable(pfx), qPrintable(fileName), lineNo, qPrintable(msg));
+ else if (lineNo)
+ fprintf(stderr, "%s%s: %s\n", qPrintable(pfx), qPrintable(fileName), qPrintable(msg));
+ else
+ fprintf(stderr, "%s%s\n", qPrintable(pfx), qPrintable(msg));
+}
+
+void EvalHandler::fileMessage(const QString &msg)
+{
+ fprintf(stderr, "%s\n", qPrintable(msg));
+}
+
+void EvalHandler::aboutToEval(ProFile *, ProFile *, EvalFileType)
+{
+}
+
+void EvalHandler::doneWithEval(ProFile *)
+{
+}
+
class QMakeCacheClearItem {
private:
qmakeCacheClearFunc func;
@@ -693,8 +662,8 @@ qmakeAddCacheClear(qmakeCacheClearFunc func, void **data)
QString qmake_libraryInfoFile()
{
- if(!Option::qmake_abslocation.isEmpty())
- return QDir(QFileInfo(Option::qmake_abslocation).absolutePath()).filePath("qt.conf");
+ if (!Option::globals->qmake_abslocation.isEmpty())
+ return QDir(QFileInfo(Option::globals->qmake_abslocation).absolutePath()).filePath("qt.conf");
return QString();
}
diff --git a/qmake/option.h b/qmake/option.h
index cab8426db0..28ac860f88 100644
--- a/qmake/option.h
+++ b/qmake/option.h
@@ -42,7 +42,10 @@
#ifndef OPTION_H
#define OPTION_H
-#include "project.h"
+#include <qmakeglobals.h>
+#include <qmakeparser.h>
+#include <qmakeevaluator.h>
+
#include <qstring.h>
#include <qstringlist.h>
#include <qfile.h>
@@ -69,8 +72,26 @@ enum QMakeWarn {
};
void warn_msg(QMakeWarn t, const char *fmt, ...);
+class QMakeProject;
+
+class EvalHandler : public QMakeHandler {
+public:
+ void message(int type, const QString &msg, const QString &fileName, int lineNo);
+
+ void fileMessage(const QString &msg);
+
+ void aboutToEval(ProFile *, ProFile *, EvalFileType);
+ void doneWithEval(ProFile *);
+};
+
struct Option
{
+ static EvalHandler evalHandler;
+
+ static QMakeGlobals *globals;
+ static ProFileCache *proFileCache;
+ static QMakeParser *parser;
+
//simply global convenience
static QString libtool_ext;
static QString pkgcfg_ext;
@@ -88,7 +109,6 @@ struct Option
static QString lex_mod;
static QString yacc_mod;
static QString dir_sep;
- static QString dirlist_sep;
static QString pro_ext;
static QString res_ext;
static char field_sep;
@@ -160,15 +180,12 @@ struct Option
static QMAKE_MODE qmake_mode;
//all modes
- static QString qmake_abslocation;
static QStringList qmake_args;
static QFile output;
static QString output_dir;
static int debug_level;
static int warn_level;
static bool recursive;
- static QStringList before_user_vars, after_user_vars;
- static QString user_template, user_template_prefix;
//QMAKE_*_PROPERTY options
struct prop {
@@ -183,17 +200,11 @@ struct Option
//QMAKE_GENERATE_MAKEFILE options
struct mkfile {
- static QString qmakespec;
- static QString xqmakespec;
- static bool do_cache;
static bool do_deps;
static bool do_mocs;
static bool do_dep_heuristics;
static bool do_preprocess;
static bool do_stub_makefile;
- static QString source_root;
- static QString build_root;
- static QString cachefile;
static int cachefile_depth;
static QStringList project_files;
};
@@ -203,7 +214,7 @@ private:
};
inline QString fixEnvVariables(const QString &x) { return Option::fixString(x, Option::FixEnvVars); }
-inline QStringList splitPathList(const QString &paths) { return paths.isEmpty() ? QStringList() : paths.split(Option::dirlist_sep); }
+inline QStringList splitPathList(const QString &paths) { return paths.isEmpty() ? QStringList() : paths.split(Option::globals->dirlist_sep); }
QT_END_NAMESPACE
diff --git a/qmake/project.cpp b/qmake/project.cpp
index 9fa9ac05a5..e369ce1b42 100644
--- a/qmake/project.cpp
+++ b/qmake/project.cpp
@@ -40,3757 +40,101 @@
****************************************************************************/
#include "project.h"
-#include "property.h"
+
#include "option.h"
-#include "cachekeys.h"
-#include "generators/metamakefile.h"
-#include <qdatetime.h>
-#include <qfile.h>
-#include <qfileinfo.h>
#include <qdir.h>
-#include <qregexp.h>
-#include <qtextstream.h>
-#include <qstack.h>
-#include <qdebug.h>
-#ifdef Q_OS_UNIX
-#include <time.h>
-#include <utime.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#elif defined(Q_OS_WIN32)
-#include <windows.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef Q_OS_WIN32
-#define QT_POPEN _popen
-#define QT_PCLOSE _pclose
-#else
-#define QT_POPEN popen
-#define QT_PCLOSE pclose
-#endif
+#include <stdio.h>
QT_BEGIN_NAMESPACE
-//expand functions
-enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
- E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
- E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND,
- E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE, E_REPLACE,
- E_SIZE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
- E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
- E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE };
-QHash<QString, ExpandFunc> qmake_expandFunctions()
-{
- static QHash<QString, ExpandFunc> *qmake_expand_functions = 0;
- if(!qmake_expand_functions) {
- qmake_expand_functions = new QHash<QString, ExpandFunc>;
- qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<QString, ExpandFunc> >, (void**)&qmake_expand_functions);
- qmake_expand_functions->insert("member", E_MEMBER);
- qmake_expand_functions->insert("first", E_FIRST);
- qmake_expand_functions->insert("last", E_LAST);
- qmake_expand_functions->insert("cat", E_CAT);
- qmake_expand_functions->insert("fromfile", E_FROMFILE);
- qmake_expand_functions->insert("eval", E_EVAL);
- qmake_expand_functions->insert("list", E_LIST);
- qmake_expand_functions->insert("sprintf", E_SPRINTF);
- qmake_expand_functions->insert("format_number", E_FORMAT_NUMBER);
- qmake_expand_functions->insert("join", E_JOIN);
- qmake_expand_functions->insert("split", E_SPLIT);
- qmake_expand_functions->insert("basename", E_BASENAME);
- qmake_expand_functions->insert("dirname", E_DIRNAME);
- qmake_expand_functions->insert("section", E_SECTION);
- qmake_expand_functions->insert("find", E_FIND);
- qmake_expand_functions->insert("system", E_SYSTEM);
- qmake_expand_functions->insert("unique", E_UNIQUE);
- qmake_expand_functions->insert("reverse", E_REVERSE);
- qmake_expand_functions->insert("quote", E_QUOTE);
- qmake_expand_functions->insert("escape_expand", E_ESCAPE_EXPAND);
- qmake_expand_functions->insert("upper", E_UPPER);
- qmake_expand_functions->insert("lower", E_LOWER);
- qmake_expand_functions->insert("re_escape", E_RE_ESCAPE);
- qmake_expand_functions->insert("val_escape", E_VAL_ESCAPE);
- qmake_expand_functions->insert("files", E_FILES);
- qmake_expand_functions->insert("prompt", E_PROMPT);
- qmake_expand_functions->insert("replace", E_REPLACE);
- qmake_expand_functions->insert("size", E_SIZE);
- qmake_expand_functions->insert("sort_depends", E_SORT_DEPENDS);
- qmake_expand_functions->insert("resolve_depends", E_RESOLVE_DEPENDS);
- qmake_expand_functions->insert("enumerate_vars", E_ENUMERATE_VARS);
- qmake_expand_functions->insert("shadowed", E_SHADOWED);
- qmake_expand_functions->insert("absolute_path", E_ABSOLUTE_PATH);
- qmake_expand_functions->insert("relative_path", E_RELATIVE_PATH);
- qmake_expand_functions->insert("clean_path", E_CLEAN_PATH);
- qmake_expand_functions->insert("system_path", E_SYSTEM_PATH);
- qmake_expand_functions->insert("shell_path", E_SHELL_PATH);
- qmake_expand_functions->insert("system_quote", E_SYSTEM_QUOTE);
- qmake_expand_functions->insert("shell_quote", E_SHELL_QUOTE);
- }
- return *qmake_expand_functions;
-}
-//replace functions
-enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
- T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
- T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
- T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD,
- T_DEBUG, T_ERROR, T_MESSAGE, T_WARNING, T_LOG,
- T_IF, T_OPTION, T_CACHE, T_MKPATH, T_WRITE_FILE, T_TOUCH };
-QHash<QString, TestFunc> qmake_testFunctions()
+QMakeProject::QMakeProject()
+ : QMakeEvaluator(Option::globals, Option::parser, &Option::evalHandler)
{
- static QHash<QString, TestFunc> *qmake_test_functions = 0;
- if(!qmake_test_functions) {
- qmake_test_functions = new QHash<QString, TestFunc>;
- qmake_test_functions->insert("requires", T_REQUIRES);
- qmake_test_functions->insert("greaterThan", T_GREATERTHAN);
- qmake_test_functions->insert("lessThan", T_LESSTHAN);
- qmake_test_functions->insert("equals", T_EQUALS);
- qmake_test_functions->insert("isEqual", T_EQUALS);
- qmake_test_functions->insert("exists", T_EXISTS);
- qmake_test_functions->insert("export", T_EXPORT);
- qmake_test_functions->insert("clear", T_CLEAR);
- qmake_test_functions->insert("unset", T_UNSET);
- qmake_test_functions->insert("eval", T_EVAL);
- qmake_test_functions->insert("CONFIG", T_CONFIG);
- qmake_test_functions->insert("if", T_IF);
- qmake_test_functions->insert("isActiveConfig", T_CONFIG);
- qmake_test_functions->insert("system", T_SYSTEM);
- qmake_test_functions->insert("return", T_RETURN);
- qmake_test_functions->insert("break", T_BREAK);
- qmake_test_functions->insert("next", T_NEXT);
- qmake_test_functions->insert("defined", T_DEFINED);
- qmake_test_functions->insert("contains", T_CONTAINS);
- qmake_test_functions->insert("infile", T_INFILE);
- qmake_test_functions->insert("count", T_COUNT);
- qmake_test_functions->insert("isEmpty", T_ISEMPTY);
- qmake_test_functions->insert("include", T_INCLUDE);
- qmake_test_functions->insert("load", T_LOAD);
- qmake_test_functions->insert("debug", T_DEBUG);
- qmake_test_functions->insert("error", T_ERROR);
- qmake_test_functions->insert("message", T_MESSAGE);
- qmake_test_functions->insert("warning", T_WARNING);
- qmake_test_functions->insert("log", T_LOG);
- qmake_test_functions->insert("option", T_OPTION);
- qmake_test_functions->insert("cache", T_CACHE);
- qmake_test_functions->insert("mkpath", T_MKPATH);
- qmake_test_functions->insert("write_file", T_WRITE_FILE);
- qmake_test_functions->insert("touch", T_TOUCH);
- }
- return *qmake_test_functions;
}
-struct parser_info {
- QString file;
- int line_no;
- bool from_file;
-} parser;
-
-static QString cached_source_root;
-static QString cached_build_root;
-static QStringList cached_qmakepath;
-static QStringList cached_qmakefeatures;
-
-static QStringList *all_feature_roots[2] = { 0, 0 };
-
-static void
-invalidateFeatureRoots()
+QMakeProject::QMakeProject(QMakeProject *p)
+ : QMakeEvaluator(Option::globals, Option::parser, &Option::evalHandler)
{
- for (int i = 0; i < 2; i++)
- if (all_feature_roots[i])
- all_feature_roots[i]->clear();
+ initFrom(*p);
}
-static QString remove_quotes(const QString &arg)
+bool QMakeProject::read(const QString &project, LoadFlags what)
{
- const ushort SINGLEQUOTE = '\'';
- const ushort DOUBLEQUOTE = '"';
-
- const QChar *arg_data = arg.data();
- const ushort first = arg_data->unicode();
- const int arg_len = arg.length();
- if(first == SINGLEQUOTE || first == DOUBLEQUOTE) {
- const ushort last = (arg_data+arg_len-1)->unicode();
- if(last == first)
- return arg.mid(1, arg_len-2);
- }
- return arg;
+ m_projectFile = project;
+ setOutputDir(Option::output_dir);
+ QString absproj = (project == QLatin1String("-"))
+ ? QLatin1String("(stdin)")
+ : QDir::cleanPath(QDir(qmake_getpwd()).absoluteFilePath(project));
+ return evaluateFile(absproj, QMakeHandler::EvalProjectFile, what);
}
-static QString varMap(const QString &x)
+static ProStringList prepareBuiltinArgs(const QList<ProStringList> &args)
{
- QString ret(x);
- if(ret == "INTERFACES")
- ret = "FORMS";
- else if(ret == "QMAKE_POST_BUILD")
- ret = "QMAKE_POST_LINK";
- else if(ret == "TARGETDEPS")
- ret = "POST_TARGETDEPS";
- else if(ret == "LIBPATH")
- ret = "QMAKE_LIBDIR";
- else if(ret == "QMAKE_EXT_MOC")
- ret = "QMAKE_EXT_CPP_MOC";
- else if(ret == "QMAKE_MOD_MOC")
- ret = "QMAKE_H_MOD_MOC";
- else if(ret == "QMAKE_LFLAGS_SHAPP")
- ret = "QMAKE_LFLAGS_APP";
- else if(ret == "PRECOMPH")
- ret = "PRECOMPILED_HEADER";
- else if(ret == "PRECOMPCPP")
- ret = "PRECOMPILED_SOURCE";
- else if(ret == "INCPATH")
- ret = "INCLUDEPATH";
- else if(ret == "QMAKE_EXTRA_WIN_COMPILERS" || ret == "QMAKE_EXTRA_UNIX_COMPILERS")
- ret = "QMAKE_EXTRA_COMPILERS";
- else if(ret == "QMAKE_EXTRA_WIN_TARGETS" || ret == "QMAKE_EXTRA_UNIX_TARGETS")
- ret = "QMAKE_EXTRA_TARGETS";
- else if(ret == "QMAKE_EXTRA_UNIX_INCLUDES")
- ret = "QMAKE_EXTRA_INCLUDES";
- else if(ret == "QMAKE_EXTRA_UNIX_VARIABLES")
- ret = "QMAKE_EXTRA_VARIABLES";
- else if(ret == "QMAKE_RPATH")
- ret = "QMAKE_LFLAGS_RPATH";
- else if(ret == "QMAKE_FRAMEWORKDIR")
- ret = "QMAKE_FRAMEWORKPATH";
- else if(ret == "QMAKE_FRAMEWORKDIR_FLAGS")
- ret = "QMAKE_FRAMEWORKPATH_FLAGS";
- else if(ret == "IN_PWD")
- ret = "PWD";
- else
- return ret;
- warn_msg(WarnDeprecated, "%s:%d: Variable %s is deprecated; use %s instead.",
- parser.file.toLatin1().constData(), parser.line_no,
- x.toLatin1().constData(), ret.toLatin1().constData());
+ ProStringList ret;
+ ret.reserve(args.size());
+ foreach (const ProStringList &arg, args)
+ ret << arg.join(" ");
return ret;
}
-static QStringList split_arg_list(const QString &params)
+bool QMakeProject::test(const ProKey &func, const QList<ProStringList> &args)
{
- int quote = 0;
- QStringList args;
+ m_current.clear();
- const ushort LPAREN = '(';
- const ushort RPAREN = ')';
- const ushort SINGLEQUOTE = '\'';
- const ushort DOUBLEQUOTE = '"';
- const ushort BACKSLASH = '\\';
- const ushort COMMA = ',';
- const ushort SPACE = ' ';
- //const ushort TAB = '\t';
+ QHash<ProKey, ProFunctionDef>::ConstIterator it =
+ m_functionDefs.testFunctions.constFind(func);
+ if (it != m_functionDefs.testFunctions.constEnd())
+ return evaluateBoolFunction(*it, args, func) == ReturnTrue;
- const QChar *params_data = params.data();
- const int params_len = params.length();
- for(int last = 0; ;) {
- while(last < params_len && (params_data[last].unicode() == SPACE
- /*|| params_data[last].unicode() == TAB*/))
- ++last;
- for(int x = last, parens = 0; ; x++) {
- if(x == params_len) {
- while(x > last && params_data[x-1].unicode() == SPACE)
- --x;
- args << params.mid(last, x - last);
- // Could do a check for unmatched parens here, but split_value_list()
- // is called on all our output, so mistakes will be caught anyway.
- return args;
- }
- ushort unicode = params_data[x].unicode();
- if(x != (int)params_len-1 && unicode == BACKSLASH &&
- (params_data[x+1].unicode() == SINGLEQUOTE || params_data[x+1].unicode() == DOUBLEQUOTE)) {
- x++; //get that 'escape'
- } else if(quote && unicode == quote) {
- quote = 0;
- } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
- quote = unicode;
- } else if(unicode == RPAREN) {
- --parens;
- } else if(unicode == LPAREN) {
- ++parens;
- }
- if(!parens && !quote && unicode == COMMA) {
- int prev = last;
- last = x+1;
- while(x > prev && params_data[x-1].unicode() == SPACE)
- --x;
- args << params.mid(prev, x - prev);
- break;
- }
- }
- }
+ return evaluateBuiltinConditional(func, prepareBuiltinArgs(args)) == ReturnTrue;
}
-static QStringList split_value_list(const QString &vals)
-{
- QString build;
- QStringList ret;
- ushort quote = 0;
- int parens = 0;
-
- const ushort LPAREN = '(';
- const ushort RPAREN = ')';
- const ushort SINGLEQUOTE = '\'';
- const ushort DOUBLEQUOTE = '"';
- const ushort BACKSLASH = '\\';
-
- ushort unicode;
- const QChar *vals_data = vals.data();
- const int vals_len = vals.length();
- for(int x = 0; x < vals_len; x++) {
- unicode = vals_data[x].unicode();
- if(x != (int)vals_len-1 && unicode == BACKSLASH &&
- (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
- build += vals_data[x++]; //get that 'escape'
- } else if(quote && unicode == quote) {
- quote = 0;
- } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
- quote = unicode;
- } else if(unicode == RPAREN) {
- --parens;
- } else if(unicode == LPAREN) {
- ++parens;
- }
-
- if(!parens && !quote && (vals_data[x] == Option::field_sep)) {
- ret << build;
- build.clear();
- } else {
- build += vals_data[x];
- }
- }
- if(!build.isEmpty())
- ret << build;
- if (parens)
- warn_msg(WarnDeprecated, "%s:%d: Unmatched parentheses are deprecated.",
- parser.file.toLatin1().constData(), parser.line_no);
- // Could do a check for unmatched quotes here, but doVariableReplaceExpand()
- // is called on all our output, so mistakes will be caught anyway.
- return ret;
-}
-
-//just a parsable entity
-struct ParsableBlock
-{
- ParsableBlock() : ref_cnt(1) { }
- virtual ~ParsableBlock() { }
-
- struct Parse {
- QString text;
- parser_info pi;
- Parse(const QString &t) : text(t){ pi = parser; }
- };
- QList<Parse> parselist;
-
- inline int ref() { return ++ref_cnt; }
- inline int deref() { return --ref_cnt; }
-
-protected:
- int ref_cnt;
- virtual bool continueBlock() = 0;
- bool eval(QMakeProject *p, QHash<QString, QStringList> &place);
-};
-
-bool ParsableBlock::eval(QMakeProject *p, QHash<QString, QStringList> &place)
+QStringList QMakeProject::expand(const ProKey &func, const QList<ProStringList> &args)
{
- //save state
- parser_info pi = parser;
- const int block_count = p->scope_blocks.count();
-
- //execute
- bool ret = true;
- for(int i = 0; i < parselist.count(); i++) {
- parser = parselist.at(i).pi;
- if(!(ret = p->parse(parselist.at(i).text, place)) || !continueBlock())
- break;
- }
-
- //restore state
- parser = pi;
- while(p->scope_blocks.count() > block_count)
- p->scope_blocks.pop();
- return ret;
-}
-
-//defined functions
-struct FunctionBlock : public ParsableBlock
-{
- FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
-
- QHash<QString, QStringList> vars;
- QHash<QString, QStringList> *calling_place;
- QStringList return_value;
- int scope_level;
- bool cause_return;
-
- bool exec(const QList<QStringList> &args,
- QMakeProject *p, QHash<QString, QStringList> &place, QStringList &functionReturn);
- virtual bool continueBlock() { return !cause_return; }
-};
-
-bool FunctionBlock::exec(const QList<QStringList> &args,
- QMakeProject *proj, QHash<QString, QStringList> &place,
- QStringList &functionReturn)
-{
- //save state
-#if 1
- calling_place = &place;
-#else
- calling_place = &proj->variables();
-#endif
- return_value.clear();
- cause_return = false;
-
- //execute
-#if 0
- vars = proj->variables(); // should be place so that local variables can be inherited
-#else
- vars = place;
-#endif
- vars["ARGS"].clear();
- for(int i = 0; i < args.count(); i++) {
- vars["ARGS"] += args[i];
- vars[QString::number(i+1)] = args[i];
- }
- bool ret = ParsableBlock::eval(proj, vars);
- functionReturn = return_value;
-
- //restore state
- calling_place = 0;
- return_value.clear();
- vars.clear();
- return ret;
-}
-
-//loops
-struct IteratorBlock : public ParsableBlock
-{
- IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
-
- int scope_level;
-
- struct Test {
- QString func;
- QStringList args;
- bool invert;
- parser_info pi;
- Test(const QString &f, QStringList &a, bool i) : func(f), args(a), invert(i) { pi = parser; }
- };
- QList<Test> test;
-
- QString variable;
-
- bool loop_forever, cause_break, cause_next;
- QStringList list;
-
- bool exec(QMakeProject *p, QHash<QString, QStringList> &place);
- virtual bool continueBlock() { return !cause_next && !cause_break; }
-};
-bool IteratorBlock::exec(QMakeProject *p, QHash<QString, QStringList> &place)
-{
- bool ret = true;
- QStringList::Iterator it;
- if(!loop_forever)
- it = list.begin();
- int iterate_count = 0;
- //save state
- IteratorBlock *saved_iterator = p->iterator;
- p->iterator = this;
-
- //do the loop
- while(loop_forever || it != list.end()) {
- cause_next = cause_break = false;
- if(!loop_forever && (*it).isEmpty()) { //ignore empty items
- ++it;
- continue;
- }
-
- //set up the loop variable
- QStringList va;
- if(!variable.isEmpty()) {
- va = place[variable];
- if(loop_forever)
- place[variable] = QStringList(QString::number(iterate_count));
- else
- place[variable] = QStringList(*it);
- }
- //do the iterations
- bool succeed = true;
- for(QList<Test>::Iterator test_it = test.begin(); test_it != test.end(); ++test_it) {
- parser = (*test_it).pi;
- succeed = p->doProjectTest((*test_it).func, (*test_it).args, place);
- if((*test_it).invert)
- succeed = !succeed;
- if(!succeed)
- break;
- }
- if(succeed)
- ret = ParsableBlock::eval(p, place);
- //restore the variable in the map
- if(!variable.isEmpty())
- place[variable] = va;
- //loop counters
- if(!loop_forever)
- ++it;
- iterate_count++;
- if(!ret || cause_break)
- break;
- }
+ m_current.clear();
- //restore state
- p->iterator = saved_iterator;
- return ret;
-}
+ QHash<ProKey, ProFunctionDef>::ConstIterator it =
+ m_functionDefs.replaceFunctions.constFind(func);
+ if (it != m_functionDefs.replaceFunctions.constEnd())
+ return evaluateFunction(*it, args, 0).toQStringList();
-QMakeProject::ScopeBlock::~ScopeBlock()
-{
-#if 0
- if(iterate)
- delete iterate;
-#endif
+ return evaluateBuiltinExpand(func, prepareBuiltinArgs(args)).toQStringList();
}
-static void qmake_error_msg(const QString &msg)
+ProString QMakeProject::expand(const QString &expr, const QString &where, int line)
{
- fprintf(stderr, "%s:%d: %s\n", parser.file.toLatin1().constData(), parser.line_no,
- msg.toLatin1().constData());
-}
-
-/*
- 1) environment variable QMAKEFEATURES (as separated by colons)
- 2) property variable QMAKEFEATURES (as separated by colons)
- 3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
- 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
- 5) your QMAKESPEC/features dir
- 6) your data_install/mkspecs/FEATURES_DIR
- 7) your QMAKESPEC/../FEATURES_DIR dir
-
- FEATURES_DIR is defined as:
-
- 1) features/(unix|win32|macx)/
- 2) features/
-*/
-QStringList QMakeProject::qmakeFeaturePaths()
-{
- const QString mkspecs_concat = QLatin1String("/mkspecs");
- const QString base_concat = QLatin1String("/features");
- QStringList concat;
- foreach (const QString &sfx, values("QMAKE_PLATFORM", vars))
- concat << base_concat + QLatin1Char('/') + sfx;
- concat << base_concat;
-
- QStringList feature_roots = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEFEATURES")));
- feature_roots += cached_qmakefeatures;
- if(prop)
- feature_roots += splitPathList(prop->value("QMAKEFEATURES").toQString());
- QStringList feature_bases;
- if (!cached_build_root.isEmpty())
- feature_bases << cached_build_root;
- if (!cached_source_root.isEmpty())
- feature_bases << cached_source_root;
- QStringList qmakepath = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEPATH")));
- qmakepath += cached_qmakepath;
- foreach (const QString &path, qmakepath)
- feature_bases << (path + mkspecs_concat);
- if (!real_spec.isEmpty()) {
- // The spec is already platform-dependent, so no subdirs here.
- feature_roots << real_spec + base_concat;
-
- // Also check directly under the root directory of the mkspecs collection
- QFileInfo specfi(real_spec);
- QDir specrootdir(specfi.absolutePath());
- while (!specrootdir.isRoot()) {
- const QString specrootpath = specrootdir.path();
- if (specrootpath.endsWith(mkspecs_concat)) {
- if (QFile::exists(specrootpath + base_concat))
- feature_bases << specrootpath;
- break;
- }
- specrootdir.cdUp();
+ ProString ret;
+ if (ProFile *pro = m_parser->parsedProBlock(expr, where, line, QMakeParser::ValueGrammar)) {
+ if (pro->isOk()) {
+ m_current.pro = pro;
+ m_current.line = 0;
+ const ushort *tokPtr = pro->tokPtr();
+ ProStringList result = expandVariableReferences(tokPtr, 1, true);
+ if (!result.isEmpty())
+ ret = result.at(0);
}
+ pro->deref();
}
- feature_bases << (QLibraryInfo::rawLocation(QLibraryInfo::HostDataPath,
- QLibraryInfo::EffectivePaths) + mkspecs_concat);
- foreach (const QString &fb, feature_bases)
- foreach (const QString &cc, concat)
- feature_roots << (fb + cc);
- feature_roots.removeDuplicates();
-
- QStringList ret;
- foreach (const QString &root, feature_roots)
- if (QFileInfo(root).exists())
- ret << root;
return ret;
}
-QStringList qmake_mkspec_paths()
-{
- QStringList ret;
- const QString concat = QLatin1String("/mkspecs");
-
- QStringList qmakepath = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEPATH")));
- qmakepath += cached_qmakepath;
- foreach (const QString &path, qmakepath)
- ret << (path + concat);
- if (!cached_build_root.isEmpty())
- ret << cached_build_root + concat;
- if (!cached_source_root.isEmpty())
- ret << cached_source_root + concat;
- ret << QLibraryInfo::rawLocation(QLibraryInfo::HostDataPath, QLibraryInfo::EffectivePaths) + concat;
- ret.removeDuplicates();
-
- return ret;
-}
-
-static void
-setTemplate(QStringList &varlist)
-{
- if (!Option::user_template.isEmpty()) { // Don't permit override
- varlist = QStringList(Option::user_template);
- } else {
- if (varlist.isEmpty())
- varlist << "app";
- else
- varlist.erase(varlist.begin() + 1, varlist.end());
- }
- if (!Option::user_template_prefix.isEmpty()
- && !varlist.first().startsWith(Option::user_template_prefix)) {
- varlist.first().prepend(Option::user_template_prefix);
- }
-}
-
-QMakeProject::~QMakeProject()
-{
- if(own_prop)
- delete prop;
- cleanup();
-}
-
-
-void
-QMakeProject::init(QMakeProperty *p)
-{
- if(!p) {
- prop = new QMakeProperty;
- own_prop = true;
- } else {
- prop = p;
- own_prop = false;
- }
- host_build = false;
- reset();
-}
-
-void
-QMakeProject::cleanup()
-{
- for (QHash<QString, FunctionBlock*>::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it)
- if (!it.value()->deref())
- delete it.value();
- replaceFunctions.clear();
- for (QHash<QString, FunctionBlock*>::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it)
- if (!it.value()->deref())
- delete it.value();
- testFunctions.clear();
-}
-
-// Duplicate project. It is *not* allowed to call the complex read() functions on the copy.
-QMakeProject::QMakeProject(QMakeProject *p, const QHash<QString, QStringList> *_vars)
-{
- init(p->properties());
- vars = _vars ? *_vars : p->vars;
- host_build = p->host_build;
- for(QHash<QString, FunctionBlock*>::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) {
- it.value()->ref();
- replaceFunctions.insert(it.key(), it.value());
- }
- for(QHash<QString, FunctionBlock*>::iterator it = p->testFunctions.begin(); it != p->testFunctions.end(); ++it) {
- it.value()->ref();
- testFunctions.insert(it.key(), it.value());
- }
-}
-
-void
-QMakeProject::reset()
-{
- // scope_blocks starts with one non-ignoring entity
- scope_blocks.clear();
- scope_blocks.push(ScopeBlock());
- iterator = 0;
- function = 0;
- backslashWarned = false;
- need_restart = false;
-}
-
-bool
-QMakeProject::parse(const QString &t, QHash<QString, QStringList> &place, int numLines)
-{
- // To preserve the integrity of any UTF-8 characters in .pro file, temporarily replace the
- // non-breaking space (0xA0) characters with another non-space character, so that
- // QString::simplified() call will not replace it with space.
- // Note: There won't be any two byte characters in .pro files, so 0x10A0 should be a safe
- // replacement character.
- static QChar nbsp(0xA0);
- static QChar nbspFix(0x01A0);
- QString s;
- if (t.indexOf(nbsp) != -1) {
- s = t;
- s.replace(nbsp, nbspFix);
- s = s.simplified();
- s.replace(nbspFix, nbsp);
- } else {
- s = t.simplified();
- }
-
- int hash_mark = s.indexOf("#");
- if(hash_mark != -1) //good bye comments
- s = s.left(hash_mark);
- if(s.isEmpty()) // blank_line
- return true;
-
- if(scope_blocks.top().ignore) {
- bool continue_parsing = false;
- // adjust scope for each block which appears on a single line
- for(int i = 0; i < s.length(); i++) {
- if(s[i] == '{') {
- scope_blocks.push(ScopeBlock(true));
- } else if(s[i] == '}') {
- if(scope_blocks.count() == 1) {
- fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- ScopeBlock sb = scope_blocks.pop();
- if(sb.iterate) {
- sb.iterate->exec(this, place);
- delete sb.iterate;
- sb.iterate = 0;
- }
- if(!scope_blocks.top().ignore) {
- debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
- parser.line_no, scope_blocks.count()+1);
- s = s.mid(i+1).trimmed();
- continue_parsing = !s.isEmpty();
- break;
- }
- }
- }
- if(!continue_parsing) {
- debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
- parser.file.toLatin1().constData(), parser.line_no);
- return true;
- }
- }
-
- if(function) {
- QString append;
- int d_off = 0;
- const QChar *d = s.unicode();
- bool function_finished = false;
- while(d_off < s.length()) {
- if(*(d+d_off) == QLatin1Char('}')) {
- function->scope_level--;
- if(!function->scope_level) {
- function_finished = true;
- break;
- }
- } else if(*(d+d_off) == QLatin1Char('{')) {
- function->scope_level++;
- }
- append += *(d+d_off);
- ++d_off;
- }
- if(!append.isEmpty())
- function->parselist.append(IteratorBlock::Parse(append));
- if(function_finished) {
- function = 0;
- s = QString(d+d_off, s.length()-d_off);
- } else {
- return true;
- }
- } else if(IteratorBlock *it = scope_blocks.top().iterate) {
- QString append;
- int d_off = 0;
- const QChar *d = s.unicode();
- bool iterate_finished = false;
- while(d_off < s.length()) {
- if(*(d+d_off) == QLatin1Char('}')) {
- it->scope_level--;
- if(!it->scope_level) {
- iterate_finished = true;
- break;
- }
- } else if(*(d+d_off) == QLatin1Char('{')) {
- it->scope_level++;
- }
- append += *(d+d_off);
- ++d_off;
- }
- if(!append.isEmpty())
- scope_blocks.top().iterate->parselist.append(IteratorBlock::Parse(append));
- if(iterate_finished) {
- scope_blocks.top().iterate = 0;
- bool ret = it->exec(this, place);
- delete it;
- if(!ret)
- return false;
- s = s.mid(d_off);
- } else {
- return true;
- }
- }
-
- QString scope, var, op;
- QStringList val;
-#define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o
- const QChar *d = s.unicode();
- int d_off = 0;
- SKIP_WS(d, d_off, s.length());
- IteratorBlock *iterator = 0;
- bool scope_failed = false, else_line = false, or_op=false;
- QChar quote = 0;
- int parens = 0, scope_count=0, start_block = 0;
- while(d_off < s.length()) {
- if(!parens) {
- if(*(d+d_off) == QLatin1Char('='))
- break;
- if(*(d+d_off) == QLatin1Char('+') || *(d+d_off) == QLatin1Char('-') ||
- *(d+d_off) == QLatin1Char('*') || *(d+d_off) == QLatin1Char('~')) {
- if(*(d+d_off+1) == QLatin1Char('=')) {
- break;
- } else if(*(d+d_off+1) == QLatin1Char(' ')) {
- const QChar *k = d+d_off+1;
- int k_off = 0;
- SKIP_WS(k, k_off, s.length()-d_off);
- if(*(k+k_off) == QLatin1Char('=')) {
- QString msg;
- qmake_error_msg(QString(d+d_off, 1) + "must be followed immediately by =");
- return false;
- }
- }
- }
- }
-
- if(!quote.isNull()) {
- if(*(d+d_off) == quote)
- quote = QChar();
- } else if(*(d+d_off) == '(') {
- ++parens;
- } else if(*(d+d_off) == ')') {
- --parens;
- } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) {
- quote = *(d+d_off);
- }
-
- if(!parens && quote.isNull() &&
- (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('{') ||
- *(d+d_off) == QLatin1Char(')') || *(d+d_off) == QLatin1Char('|'))) {
- scope_count++;
- scope = var.trimmed();
- if(*(d+d_off) == QLatin1Char(')'))
- scope += *(d+d_off); // need this
- var = "";
-
- bool test = scope_failed;
- if(scope.isEmpty()) {
- test = true;
- } else if(scope.toLower() == "else") { //else is a builtin scope here as it modifies state
- if(scope_count != 1 || scope_blocks.top().else_status == ScopeBlock::TestNone) {
- qmake_error_msg(("Unexpected " + scope + " ('" + s + "')").toLatin1());
- return false;
- }
- else_line = true;
- test = (scope_blocks.top().else_status == ScopeBlock::TestSeek);
- debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.toLatin1().constData(), parser.line_no,
- scope == "else" ? "" : QString(" (" + scope + ")").toLatin1().constData(),
- test ? "considered" : "excluded");
- } else {
- QString comp_scope = scope;
- bool invert_test = (comp_scope.at(0) == QLatin1Char('!'));
- if(invert_test)
- comp_scope = comp_scope.mid(1);
- int lparen = comp_scope.indexOf('(');
- if(or_op == scope_failed) {
- if(lparen != -1) { // if there is an lparen in the scope, it IS a function
- int rparen = comp_scope.lastIndexOf(')');
- if(rparen == -1) {
- qmake_error_msg("Function missing right paren: " + comp_scope);
- return false;
- }
- QString func = comp_scope.left(lparen);
- QStringList args = split_arg_list(comp_scope.mid(lparen+1, rparen - lparen - 1));
- if(function) {
- fprintf(stderr, "%s:%d: No tests can come after a function definition!\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- } else if(func == "for") { //for is a builtin function here, as it modifies state
- if(args.count() > 2 || args.count() < 1) {
- fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- } else if(iterator) {
- fprintf(stderr, "%s:%d unexpected nested for()\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
-
- iterator = new IteratorBlock;
- QString it_list;
- if(args.count() == 1) {
- doVariableReplace(args[0], place);
- it_list = args[0];
- if(args[0] != "ever") {
- delete iterator;
- iterator = 0;
- fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- it_list = "forever";
- } else if(args.count() == 2) {
- iterator->variable = args[0];
- doVariableReplace(args[1], place);
- it_list = args[1];
- }
- QStringList list = place[it_list];
- if(list.isEmpty()) {
- if(it_list == "forever") {
- iterator->loop_forever = true;
- } else {
- int dotdot = it_list.indexOf("..");
- if(dotdot != -1) {
- bool ok;
- int start = it_list.left(dotdot).toInt(&ok);
- if(ok) {
- int end = it_list.mid(dotdot+2).toInt(&ok);
- if(ok) {
- if(start < end) {
- for(int i = start; i <= end; i++)
- list << QString::number(i);
- } else {
- for(int i = start; i >= end; i--)
- list << QString::number(i);
- }
- }
- }
- }
- }
- }
- iterator->list = list;
- test = !invert_test;
- } else if(iterator) {
- iterator->test.append(IteratorBlock::Test(func, args, invert_test));
- test = !invert_test;
- } else if(func == "defineTest" || func == "defineReplace") {
- if(!function_blocks.isEmpty()) {
- fprintf(stderr,
- "%s:%d: cannot define a function within another definition.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: %s(function_name) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
- return false;
- }
- QHash<QString, FunctionBlock*> *map = 0;
- if(func == "defineTest")
- map = &testFunctions;
- else
- map = &replaceFunctions;
-#if 0
- if(!map || map->contains(args[0])) {
- fprintf(stderr, "%s:%d: Function[%s] multiply defined.\n",
- parser.file.toLatin1().constData(), parser.line_no, args[0].toLatin1().constData());
- return false;
- }
-#endif
- function = new FunctionBlock;
- map->insert(args[0], function);
- test = true;
- } else {
- test = doProjectTest(func, args, place);
- if(*(d+d_off) == QLatin1Char(')') && d_off == s.length()-1) {
- if(invert_test)
- test = !test;
- scope_blocks.top().else_status =
- (test ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
- return true; // assume we are done
- }
- }
- } else {
- QString cscope = comp_scope.trimmed();
- doVariableReplace(cscope, place);
- test = isActiveConfig(cscope.trimmed(), true, &place);
- }
- if(invert_test)
- test = !test;
- }
- }
- if(!test && !scope_failed)
- debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.toLatin1().constData(),
- parser.line_no, scope.toLatin1().constData());
- if(test == or_op)
- scope_failed = !test;
- or_op = (*(d+d_off) == QLatin1Char('|'));
-
- if(*(d+d_off) == QLatin1Char('{')) { // scoping block
- start_block++;
- if(iterator) {
- for(int off = 0, braces = 0; true; ++off) {
- if(*(d+d_off+off) == QLatin1Char('{'))
- ++braces;
- else if(*(d+d_off+off) == QLatin1Char('}') && braces)
- --braces;
- if(!braces || d_off+off == s.length()) {
- iterator->parselist.append(s.mid(d_off, off-1));
- if(braces > 1)
- iterator->scope_level += braces-1;
- d_off += off-1;
- break;
- }
- }
- }
- }
- } else if(!parens && *(d+d_off) == QLatin1Char('}')) {
- if(start_block) {
- --start_block;
- } else if(!scope_blocks.count()) {
- warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.toLatin1().constData(), parser.line_no);
- } else {
- if(scope_blocks.count() == 1) {
- fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
- parser.line_no, scope_blocks.count());
- ScopeBlock sb = scope_blocks.pop();
- if(sb.iterate)
- sb.iterate->exec(this, place);
- }
- } else {
- var += *(d+d_off);
- }
- ++d_off;
- }
- var = var.trimmed();
-
- if(!else_line || (else_line && !scope_failed))
- scope_blocks.top().else_status = (!scope_failed ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
- if(start_block) {
- ScopeBlock next_block(scope_failed);
- next_block.iterate = iterator;
- if(iterator)
- next_block.else_status = ScopeBlock::TestNone;
- else if(scope_failed)
- next_block.else_status = ScopeBlock::TestSeek;
- else
- next_block.else_status = ScopeBlock::TestFound;
- scope_blocks.push(next_block);
- debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser.file.toLatin1().constData(),
- parser.line_no, scope_blocks.count(), scope_failed, s.toLatin1().constData());
- } else if(iterator) {
- iterator->parselist.append(QString(var+s.mid(d_off)));
- bool ret = iterator->exec(this, place);
- delete iterator;
- return ret;
- }
-
- if((!scope_count && !var.isEmpty()) || (scope_count == 1 && else_line))
- scope_blocks.top().else_status = ScopeBlock::TestNone;
- if(d_off == s.length()) {
- if(!var.trimmed().isEmpty())
- qmake_error_msg(("Parse Error ('" + s + "')").toLatin1());
- return var.isEmpty(); // allow just a scope
- }
-
- SKIP_WS(d, d_off, s.length());
- for(; d_off < s.length() && op.indexOf('=') == -1; op += *(d+(d_off++)))
- ;
- op.replace(QRegExp("\\s"), "");
-
- SKIP_WS(d, d_off, s.length());
- QString vals = s.mid(d_off); // vals now contains the space separated list of values
- int rbraces = vals.count('}'), lbraces = vals.count('{');
- if(scope_blocks.count() > 1 && rbraces - lbraces == 1 && vals.endsWith('}')) {
- debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
- parser.line_no, scope_blocks.count());
- ScopeBlock sb = scope_blocks.pop();
- if(sb.iterate)
- sb.iterate->exec(this, place);
- vals.truncate(vals.length()-1);
- } else if(rbraces != lbraces) {
- warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d",
- vals.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
- }
- if(scope_failed)
- return true; // oh well
-#undef SKIP_WS
-
- doVariableReplace(var, place);
- var = varMap(var); //backwards compatibility
-
- if(vals.contains('=') && numLines > 1)
- warn_msg(WarnParser, "Possible accidental line continuation: {%s} at %s:%d",
- var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
-
- QStringList &varlist = place[var]; // varlist is the list in the symbol table
-
- if(Option::debug_level >= 1) {
- QString tmp_vals = vals;
- doVariableReplace(tmp_vals, place);
- debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.toLatin1().constData(), parser.line_no,
- var.toLatin1().constData(), op.toLatin1().constData(), tmp_vals.toLatin1().constData());
- }
-
- // now do the operation
- if(op == "~=") {
- doVariableReplace(vals, place);
- if(vals.length() < 4 || vals.at(0) != 's') {
- qmake_error_msg(("~= operator only can handle s/// function ('" +
- s + "')").toLatin1());
- return false;
- }
- QChar sep = vals.at(1);
- QStringList func = vals.split(sep);
- if(func.count() < 3 || func.count() > 4) {
- qmake_error_msg(("~= operator only can handle s/// function ('" +
- s + "')").toLatin1());
- return false;
- }
- bool global = false, case_sense = true, quote = false;
- if(func.count() == 4) {
- global = func[3].indexOf('g') != -1;
- case_sense = func[3].indexOf('i') == -1;
- quote = func[3].indexOf('q') != -1;
- }
- QString from = func[1], to = func[2];
- if(quote)
- from = QRegExp::escape(from);
- QRegExp regexp(from, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
- for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) {
- if((*varit).contains(regexp)) {
- (*varit) = (*varit).replace(regexp, to);
- if ((*varit).isEmpty())
- varit = varlist.erase(varit);
- else
- ++varit;
- if(!global)
- break;
- } else
- ++varit;
- }
- } else {
- QStringList vallist;
- {
- //doVariableReplace(vals, place);
- QStringList tmp = split_value_list(vals);
- for(int i = 0; i < tmp.size(); ++i)
- vallist += doVariableReplaceExpand(tmp[i], place);
- }
-
- if(op == "=") {
-#if 0 // This is way too noisy
- if(!varlist.isEmpty()) {
- bool send_warning = false;
- if(var != "TEMPLATE" && var != "TARGET") {
- QSet<QString> incoming_vals = vallist.toSet();
- for(int i = 0; i < varlist.size(); ++i) {
- const QString var = varlist.at(i).trimmed();
- if(!var.isEmpty() && !incoming_vals.contains(var)) {
- send_warning = true;
- break;
- }
- }
- }
- if(send_warning)
- warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d",
- var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
- }
-#endif
- varlist.clear();
- }
- for(QStringList::ConstIterator valit = vallist.begin();
- valit != vallist.end(); ++valit) {
- if((*valit).isEmpty())
- continue;
- if((op == "*=" && !varlist.contains((*valit))) ||
- op == "=" || op == "+=")
- varlist.append((*valit));
- else if(op == "-=")
- varlist.removeAll((*valit));
- }
- if(var == "REQUIRES") // special case to get communicated to backends!
- doProjectCheckReqs(vallist, place);
- else if (var == QLatin1String("TEMPLATE"))
- setTemplate(varlist);
- }
- return true;
-}
-
-bool
-QMakeProject::read(QTextStream &file, QHash<QString, QStringList> &place)
-{
- int numLines = 0;
- bool ret = true;
- QString s;
- while(!file.atEnd()) {
- parser.line_no++;
- QString line = file.readLine().trimmed();
- int prelen = line.length();
-
- int hash_mark = line.indexOf("#");
- if(hash_mark != -1) //good bye comments
- line = line.left(hash_mark).trimmed();
- if(!line.isEmpty() && line.right(1) == "\\") {
- if(!line.startsWith("#")) {
- line.truncate(line.length() - 1);
- s += line + Option::field_sep;
- ++numLines;
- }
- } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) {
- if(s.isEmpty() && line.isEmpty())
- continue;
- if(!line.isEmpty()) {
- s += line;
- ++numLines;
- }
- if(!s.isEmpty()) {
- if(!(ret = parse(s, place, numLines))) {
- s = "";
- numLines = 0;
- break;
- }
- s = "";
- numLines = 0;
- if (need_restart)
- break;
- }
- }
- }
- if (!s.isEmpty())
- ret = parse(s, place, numLines);
- return ret;
-}
-
-bool
-QMakeProject::read(const QString &file, QHash<QString, QStringList> &place)
-{
- parser_info pi = parser;
- reset();
-
- const QString oldpwd = qmake_getpwd();
- QString filename = Option::normalizePath(file, false);
- bool ret = false;
- QFile qfile;
- if (QFileInfo(file).isDir()) {
- return false;
- } else {
- qfile.setFileName(filename);
- ret = qfile.open(QIODevice::ReadOnly);
- qmake_setpwd(QFileInfo(filename).absolutePath());
- }
- if(ret) {
- place["PWD"] = QStringList(qmake_getpwd());
- parser_info pi = parser;
- parser.from_file = true;
- parser.file = filename;
- parser.line_no = 0;
- if (qfile.peek(3) == QByteArray("\xef\xbb\xbf")) {
- //UTF-8 BOM will cause subtle errors
- qmake_error_msg("Unexpected UTF-8 BOM found");
- ret = false;
- } else {
- QTextStream t(&qfile);
- ret = read(t, place);
- }
- qfile.close();
- }
- if (!need_restart && scope_blocks.count() != 1) {
- qmake_error_msg("Unterminated conditional block at end of file");
- ret = false;
- }
- parser = pi;
- qmake_setpwd(oldpwd);
- return ret;
-}
-
-bool
-QMakeProject::read(const QString &project, uchar cmd)
-{
- pfile = QFileInfo(project).absoluteFilePath();
- return read(cmd);
-}
-
-bool
-QMakeProject::read(uchar cmd)
-{
- again:
- if (init_vars.isEmpty()) {
- loadDefaults();
- init_vars = vars;
- } else {
- vars = init_vars;
- }
- if (cmd & ReadSetup) {
- if (base_vars.isEmpty()) {
- QString superdir;
- QString project_root;
- QStringList qmakepath;
- QStringList qmakefeatures;
- project_build_root.clear();
- if (Option::mkfile::do_cache) { // parse the cache
- QHash<QString, QStringList> cache;
- QString rdir = Option::output_dir;
- forever {
- QFileInfo qfi(rdir, QLatin1String(".qmake.super"));
- if (qfi.exists()) {
- superfile = qfi.filePath();
- if (!read(superfile, cache))
- return false;
- superdir = rdir;
- break;
- }
- QFileInfo qdfi(rdir);
- if (qdfi.isRoot())
- break;
- rdir = qdfi.path();
- }
- if (Option::mkfile::cachefile.isEmpty()) { //find it as it has not been specified
- QString sdir = qmake_getpwd();
- QString dir = Option::output_dir;
- forever {
- QFileInfo qsfi(sdir, QLatin1String(".qmake.conf"));
- if (qsfi.exists()) {
- conffile = qsfi.filePath();
- if (!read(conffile, cache))
- return false;
- }
- QFileInfo qfi(dir, QLatin1String(".qmake.cache"));
- if (qfi.exists()) {
- cachefile = qfi.filePath();
- if (!read(cachefile, cache))
- return false;
- }
- if (!conffile.isEmpty() || !cachefile.isEmpty()) {
- project_root = sdir;
- project_build_root = dir;
- break;
- }
- if (dir == superdir)
- break;
- QFileInfo qsdfi(sdir);
- QFileInfo qdfi(dir);
- if (qsdfi.isRoot() || qdfi.isRoot())
- break;
- sdir = qsdfi.path();
- dir = qdfi.path();
- }
- } else {
- QFileInfo fi(Option::mkfile::cachefile);
- cachefile = QDir::cleanPath(fi.absoluteFilePath());
- if (!read(cachefile, cache))
- return false;
- project_build_root = QDir::cleanPath(fi.absolutePath());
- // This intentionally bypasses finding a source root,
- // as the result would be more or less arbitrary.
- }
-
- if (Option::mkfile::xqmakespec.isEmpty() && !cache["XQMAKESPEC"].isEmpty())
- Option::mkfile::xqmakespec = cache["XQMAKESPEC"].first();
- if (Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty()) {
- Option::mkfile::qmakespec = cache["QMAKESPEC"].first();
- if (Option::mkfile::xqmakespec.isEmpty())
- Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
- }
- qmakepath = cache.value(QLatin1String("QMAKEPATH"));
- qmakefeatures = cache.value(QLatin1String("QMAKEFEATURES"));
-
- if (!superfile.isEmpty())
- vars["_QMAKE_SUPER_CACHE_"] << superfile;
- if (!cachefile.isEmpty())
- vars["_QMAKE_CACHE_"] << cachefile;
- }
-
- // Look for mkspecs/ in source and build. First to win determines the root.
- QString sdir = qmake_getpwd();
- QString dir = Option::output_dir;
- while (dir != project_build_root) {
- if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir())
- || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) {
- if (dir != sdir)
- project_root = sdir;
- project_build_root = dir;
- break;
- }
- if (dir == superdir)
- break;
- QFileInfo qsdfi(sdir);
- QFileInfo qdfi(dir);
- if (qsdfi.isRoot() || qdfi.isRoot())
- break;
- sdir = qsdfi.path();
- dir = qdfi.path();
- }
-
- if (qmakepath != cached_qmakepath || qmakefeatures != cached_qmakefeatures
- || project_build_root != cached_build_root) { // No need to check source dir, as it goes in sync
- cached_source_root = project_root;
- cached_build_root = project_build_root;
- cached_qmakepath = qmakepath;
- cached_qmakefeatures = qmakefeatures;
- invalidateFeatureRoots();
- }
-
- { // parse mkspec
- QString *specp = host_build ? &Option::mkfile::qmakespec : &Option::mkfile::xqmakespec;
- QString qmakespec = *specp;
- if (qmakespec.isEmpty())
- qmakespec = host_build ? "default-host" : "default";
- if (QDir::isRelativePath(qmakespec)) {
- QStringList mkspec_roots = qmake_mkspec_paths();
- debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(),
- mkspec_roots.join("::").toLatin1().constData());
- bool found_mkspec = false;
- for (QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
- QString mkspec = (*it) + QLatin1Char('/') + qmakespec;
- if (QFile::exists(mkspec)) {
- found_mkspec = true;
- *specp = qmakespec = mkspec;
- break;
- }
- }
- if (!found_mkspec) {
- fprintf(stderr, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
- qmakespec.toLatin1().constData(), mkspec_roots.join("\n\t").toLatin1().constData());
- return false;
- }
- }
-
- // We do this before reading the spec, so it can use module and feature paths from
- // here without resorting to tricks. This is the only planned use case anyway.
- if (!superfile.isEmpty()) {
- debug_msg(1, "Project super cache file: reading %s", superfile.toLatin1().constData());
- read(superfile, vars);
- }
-
- // parse qmake configuration
- doProjectInclude("spec_pre", IncludeFlagFeature, vars);
- while(qmakespec.endsWith(QLatin1Char('/')))
- qmakespec.truncate(qmakespec.length()-1);
- QString spec = qmakespec + QLatin1String("/qmake.conf");
- debug_msg(1, "QMAKESPEC conf: reading %s", spec.toLatin1().constData());
- if (!read(spec, vars)) {
- fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.toLatin1().constData());
- return false;
- }
-#ifdef Q_OS_UNIX
- real_spec = QFileInfo(qmakespec).canonicalFilePath();
-#else
- // 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_ORG variable.
- QString orig_spec = first(ProKey("QMAKESPEC_ORIGINAL")).toQString();
- real_spec = orig_spec.isEmpty() ? qmakespec : orig_spec;
-#endif
- vars["QMAKESPEC"] << real_spec;
- short_spec = QFileInfo(real_spec).fileName();
- doProjectInclude("spec_post", IncludeFlagFeature, vars);
- // The spec extends the feature search path, so invalidate the cache.
- invalidateFeatureRoots();
- // The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
- Option::dir_sep = first("QMAKE_DIR_SEP").toQString();
-
- if (!conffile.isEmpty()) {
- debug_msg(1, "Project config file: reading %s", conffile.toLatin1().constData());
- read(conffile, vars);
- }
- if (!cachefile.isEmpty()) {
- debug_msg(1, "QMAKECACHE file: reading %s", cachefile.toLatin1().constData());
- read(cachefile, vars);
- }
- }
-
- base_vars = vars;
- } else {
- vars = base_vars; // start with the base
- }
- setupProject();
- } else if (cmd & ReadFeatures) {
- // Even when ReadSetup is not set, but ReadFeatures is,
- // we still need to process spec_pre.prf to load some
- // default values and other settings.
- debug_msg(1, "Processing spec_pre (but skipping actual spec): %s", vars["CONFIG"].join("::").toLatin1().constData());
- doProjectInclude("spec_pre", IncludeFlagFeature, vars);
- }
-
- for (ProValueMap::ConstIterator it = extra_vars.constBegin();
- it != extra_vars.constEnd(); ++it)
- vars.insert(it.key().toQString(), it.value().toQStringList());
-
- if(cmd & ReadFeatures) {
- debug_msg(1, "Processing default_pre: %s", vars["CONFIG"].join("::").toLatin1().constData());
- doProjectInclude("default_pre", IncludeFlagFeature, vars);
- }
-
- //before commandline
- if (cmd & ReadSetup) {
- parser.file = "(internal)";
- parser.from_file = false;
- parser.line_no = 1; //really arg count now.. duh
- reset();
- for(QStringList::ConstIterator it = Option::before_user_vars.begin();
- it != Option::before_user_vars.end(); ++it) {
- if(!parse((*it), vars)) {
- fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
- return false;
- }
- parser.line_no++;
- }
- }
-
- // After user configs, to override them
- if (!extra_configs.isEmpty()) {
- parser.file = "(extra configs)";
- parser.from_file = false;
- parser.line_no = 1; //really arg count now.. duh
- parse("CONFIG += " + extra_configs.join(" "), vars);
- }
-
- if(cmd & ReadProFile) { // parse project file
- debug_msg(1, "Project file: reading %s", pfile.toLatin1().constData());
- if (!QFile::exists(pfile) && !pfile.endsWith(Option::pro_ext))
- pfile += Option::pro_ext;
- if(!read(pfile, vars))
- return false;
- if (need_restart) {
- base_vars.clear();
- cleanup();
- goto again;
- }
- }
-
- if (cmd & ReadSetup) {
- parser.file = "(internal)";
- parser.from_file = false;
- parser.line_no = 1; //really arg count now.. duh
- reset();
- for(QStringList::ConstIterator it = Option::after_user_vars.begin();
- it != Option::after_user_vars.end(); ++it) {
- if(!parse((*it), vars)) {
- fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
- return false;
- }
- parser.line_no++;
- }
- }
-
- // 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.
- if (!extra_configs.isEmpty()) {
- parser.file = "(extra configs)";
- parser.from_file = false;
- parser.line_no = 1; //really arg count now.. duh
- parse("CONFIG += " + extra_configs.join(" "), vars);
- }
-
- if(cmd & ReadFeatures) {
- debug_msg(1, "Processing default_post: %s", vars["CONFIG"].join("::").toLatin1().constData());
- doProjectInclude("default_post", IncludeFlagFeature, vars);
-
- QHash<QString, bool> processed;
- const QStringList &configs = vars["CONFIG"];
- debug_msg(1, "Processing CONFIG features: %s", configs.join("::").toLatin1().constData());
- while(1) {
- bool finished = true;
- for(int i = configs.size()-1; i >= 0; --i) {
- const QString config = configs[i].toLower();
- if(!processed.contains(config)) {
- processed.insert(config, true);
- if(doProjectInclude(config, IncludeFlagFeature, vars) == IncludeSuccess) {
- finished = false;
- break;
- }
- }
- }
- if(finished)
- break;
- }
- }
- return true;
-}
-
-void
-QMakeProject::setupProject()
-{
- setTemplate(vars["TEMPLATE"]);
- vars["TARGET"] << QFileInfo(pfile).baseName();
- vars["_PRO_FILE_"] << pfile;
- vars["_PRO_FILE_PWD_"] << (pfile.isEmpty() ? qmake_getpwd() : QFileInfo(pfile).absolutePath());
- vars["OUT_PWD"] << Option::output_dir;
-}
-
-void
-QMakeProject::loadDefaults()
-{
- vars["LITERAL_WHITESPACE"] << QLatin1String("\t");
- vars["LITERAL_DOLLAR"] << QLatin1String("$");
- vars["LITERAL_HASH"] << QLatin1String("#");
- vars["DIR_SEPARATOR"] << Option::dir_sep;
- vars["DIRLIST_SEPARATOR"] << Option::dirlist_sep;
- vars["QMAKE_QMAKE"] << Option::qmake_abslocation;
- vars["_DATE_"] << QDateTime::currentDateTime().toString();
-#if defined(Q_OS_WIN32)
- vars["QMAKE_HOST.os"] << QString::fromLatin1("Windows");
-
- DWORD name_length = 1024;
- wchar_t name[1024];
- if (GetComputerName(name, &name_length))
- vars["QMAKE_HOST.name"] << QString::fromWCharArray(name);
-
- QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
- vars["QMAKE_HOST.version"] << QString::number(ver);
- QString verStr;
- switch (ver) {
- case QSysInfo::WV_Me: verStr = QLatin1String("WinMe"); break;
- case QSysInfo::WV_95: verStr = QLatin1String("Win95"); break;
- case QSysInfo::WV_98: verStr = QLatin1String("Win98"); break;
- case QSysInfo::WV_NT: verStr = QLatin1String("WinNT"); break;
- case QSysInfo::WV_2000: verStr = QLatin1String("Win2000"); break;
- case QSysInfo::WV_2003: verStr = QLatin1String("Win2003"); break;
- case QSysInfo::WV_XP: verStr = QLatin1String("WinXP"); break;
- case QSysInfo::WV_VISTA: verStr = QLatin1String("WinVista"); break;
- default: verStr = QLatin1String("Unknown"); break;
- }
- vars["QMAKE_HOST.version_string"] << verStr;
-
- SYSTEM_INFO info;
- GetSystemInfo(&info);
- QString archStr;
- switch (info.wProcessorArchitecture) {
-# ifdef PROCESSOR_ARCHITECTURE_AMD64
- case PROCESSOR_ARCHITECTURE_AMD64:
- archStr = QLatin1String("x86_64");
- break;
-# endif
- case PROCESSOR_ARCHITECTURE_INTEL:
- archStr = QLatin1String("x86");
- break;
- case PROCESSOR_ARCHITECTURE_IA64:
-# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
- case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
-# endif
- archStr = QLatin1String("IA64");
- break;
- default:
- archStr = QLatin1String("Unknown");
- break;
- }
- vars["QMAKE_HOST.arch"] << archStr;
-
-# if defined(Q_CC_MSVC)
- QString paths = QString::fromLocal8Bit(qgetenv("PATH"));
- QString vcBin64 = QString::fromLocal8Bit(qgetenv("VCINSTALLDIR"));
- if (!vcBin64.endsWith('\\'))
- vcBin64.append('\\');
- vcBin64.append("bin\\amd64");
- QString vcBinX86_64 = QString::fromLocal8Bit(qgetenv("VCINSTALLDIR"));
- if (!vcBinX86_64.endsWith('\\'))
- vcBinX86_64.append('\\');
- vcBinX86_64.append("bin\\x86_amd64");
- if (paths.contains(vcBin64,Qt::CaseInsensitive) || paths.contains(vcBinX86_64,Qt::CaseInsensitive))
- vars["QMAKE_TARGET.arch"] << QString::fromLatin1("x86_64");
- else
- vars["QMAKE_TARGET.arch"] << QString::fromLatin1("x86");
-# endif
-#elif defined(Q_OS_UNIX)
- struct utsname name;
- if (!uname(&name)) {
- vars["QMAKE_HOST.os"] << QString::fromLocal8Bit(name.sysname);
- vars["QMAKE_HOST.name"] << QString::fromLocal8Bit(name.nodename);
- vars["QMAKE_HOST.version"] << QString::fromLocal8Bit(name.release);
- vars["QMAKE_HOST.version_string"] << QString::fromLocal8Bit(name.version);
- vars["QMAKE_HOST.arch"] << QString::fromLocal8Bit(name.machine);
- }
-#endif
-}
-
-bool
-QMakeProject::isActiveConfig(const QString &x, bool regex, QHash<QString, QStringList> *place)
-{
- if(x.isEmpty())
- return true;
-
- //magic types for easy flipping
- if(x == "true")
- return true;
- else if(x == "false")
- return false;
-
- if (x == "host_build")
- return host_build;
-
- //mkspecs
- QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard);
- if ((regex && re.exactMatch(short_spec)) || (!regex && short_spec == x))
- return true;
-
- //simple matching
- const QStringList &configs = (place ? (*place)["CONFIG"] : vars["CONFIG"]);
- for(QStringList::ConstIterator it = configs.begin(); it != configs.end(); ++it) {
- if(((regex && re.exactMatch((*it))) || (!regex && (*it) == x)) && re.exactMatch((*it)))
- return true;
- }
- return false;
-}
-
-bool
-QMakeProject::doProjectTest(QString str, QHash<QString, QStringList> &place)
-{
- QString chk = remove_quotes(str);
- if(chk.isEmpty())
- return true;
- bool invert_test = (chk.left(1) == "!");
- if(invert_test)
- chk = chk.mid(1);
-
- bool test=false;
- int lparen = chk.indexOf('(');
- if(lparen != -1) { // if there is an lparen in the chk, it IS a function
- int rparen = chk.indexOf(')', lparen);
- if(rparen == -1) {
- qmake_error_msg("Function missing right paren: " + chk);
- } else {
- QString func = chk.left(lparen);
- test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place);
- }
- } else {
- test = isActiveConfig(chk, true, &place);
- }
- if(invert_test)
- return !test;
- return test;
-}
-
-bool
-QMakeProject::doProjectTest(QString func, const QString &params,
- QHash<QString, QStringList> &place)
-{
- return doProjectTest(func, split_arg_list(params), place);
-}
-
-QMakeProject::IncludeStatus
-QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QStringList> &place)
-{
- if(flags & IncludeFlagFeature) {
- if(!file.endsWith(Option::prf_ext))
- file += Option::prf_ext;
- {
- QStringList *&feature_roots = all_feature_roots[host_build];
- if(!feature_roots) {
- feature_roots = new QStringList;
- qmakeAddCacheClear(qmakeDeleteCacheClear<QStringList>, (void**)&feature_roots);
- }
- if (feature_roots->isEmpty())
- *feature_roots = qmakeFeaturePaths();
- debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(),
- feature_roots->join("::").toLatin1().constData());
- int start_root = 0;
- if(parser.from_file) {
- QFileInfo currFile(parser.file), prfFile(file);
- if(currFile.fileName() == prfFile.fileName()) {
- currFile = QFileInfo(currFile.canonicalFilePath());
- for(int root = 0; root < feature_roots->size(); ++root) {
- prfFile = QFileInfo(feature_roots->at(root) +
- QLatin1Char('/') + file).canonicalFilePath();
- if(prfFile == currFile) {
- start_root = root+1;
- break;
- }
- }
- }
- }
- for(int root = start_root; root < feature_roots->size(); ++root) {
- QString prf(feature_roots->at(root) + QLatin1Char('/') + file);
- if (QFile::exists(prf)) {
- file = prf;
- goto foundf;
- }
- }
- return IncludeNoExist;
- foundf: ;
- }
- if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1)
- return IncludeFeatureAlreadyLoaded;
- place["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file);
- } else if (QDir::isRelativePath(file)) {
- QStringList include_roots;
- if(Option::output_dir != qmake_getpwd())
- include_roots << qmake_getpwd();
- include_roots << Option::output_dir;
- for(int root = 0; root < include_roots.size(); ++root) {
- QString testName = QDir::fromNativeSeparators(include_roots[root]);
- if (!testName.endsWith(QLatin1Char('/')))
- testName += QLatin1Char('/');
- testName += file;
- if(QFile::exists(testName)) {
- file = testName;
- goto foundi;
- }
- }
- return IncludeNoExist;
- foundi: ;
- } else if (!QFile::exists(file)) {
- return IncludeNoExist;
- }
- debug_msg(1, "Project Parser: %s'ing file %s.", (flags & IncludeFlagFeature) ? "load" : "include",
- file.toLatin1().constData());
-
- QString orig_file = file;
- int di = file.lastIndexOf(QLatin1Char('/'));
- QString oldpwd = qmake_getpwd();
- if(di != -1) {
- if(!qmake_setpwd(file.left(file.lastIndexOf(QLatin1Char('/'))))) {
- fprintf(stderr, "Cannot find directory: %s\n", file.left(di).toLatin1().constData());
- return IncludeFailure;
- }
- }
- bool parsed = false;
- parser_info pi = parser;
- {
- if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) {
- // The "project's variables" are used in other places (eg. export()) so it's not
- // possible to use "place" everywhere. Instead just set variables and grab them later
- QMakeProject proj(prop);
- if(flags & IncludeFlagNewParser) {
- parsed = proj.read(file, proj.vars); // parse just that file (fromfile, infile)
- } else {
- parsed = proj.read(file); // parse all aux files (load/include into)
- }
- place = proj.vars;
- } else {
- QStack<ScopeBlock> sc = scope_blocks;
- IteratorBlock *it = iterator;
- FunctionBlock *fu = function;
- parsed = read(file, place);
- iterator = it;
- function = fu;
- scope_blocks = sc;
- }
- }
- if(parsed) {
- if(place["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file) == -1)
- place["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file);
- } else {
- warn_msg(WarnParser, "%s:%d: Failure to include file %s.",
- pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData());
- }
- parser = pi;
- qmake_setpwd(oldpwd);
- place["PWD"] = QStringList(qmake_getpwd());
- if(!parsed)
- return IncludeParseFailure;
- return IncludeSuccess;
-}
-
-static void
-subAll(QStringList *val, const QStringList &diffval)
-{
- foreach (const QString &dv, diffval)
- val->removeAll(dv);
-}
-
-inline static
-bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
-{
- for (int x = arg.length() - 1; x >= 0; --x) {
- ushort c = arg.unicode()[x].unicode();
- if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
- return true;
- }
- return false;
-}
-
-static QString
-shellQuoteUnix(const QString &arg)
-{
- // Chars that should be quoted (TM). This includes:
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
- 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
- }; // 0-32 \'"$`<>|;&(){}*?#!~[]
-
- if (!arg.length())
- return QString::fromLatin1("\"\"");
-
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
- ret.prepend(QLatin1Char('\''));
- ret.append(QLatin1Char('\''));
- }
- return ret;
-}
-
-static QString
-shellQuoteWin(const QString &arg)
-{
- // Chars that should be quoted (TM). This includes:
- // - control chars & space
- // - the shell meta chars "&()<>^|
- // - the potential separators ,;=
- static const uchar iqm[] = {
- 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
- 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
- };
-
- if (!arg.length())
- return QString::fromLatin1("\"\"");
-
- QString ret(arg);
- if (hasSpecialChars(ret, iqm)) {
- // Quotes are escaped and their preceding backslashes are doubled.
- // It's impossible to escape anything inside a quoted string on cmd
- // level, so the outer quoting must be "suspended".
- ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
- // The argument must not end with a \ since this would be interpreted
- // as escaping the quote -- rather put the \ behind the quote: e.g.
- // rather use "foo"\ than "foo\"
- int i = ret.length();
- while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
- --i;
- ret.insert(i, QLatin1Char('"'));
- ret.prepend(QLatin1Char('"'));
- }
- return ret;
-}
-
-static QString
-quoteValue(const QString &val)
-{
- QString ret;
- ret.reserve(val.length());
- bool quote = val.isEmpty();
- bool escaping = false;
- for (int i = 0, l = val.length(); i < l; i++) {
- QChar c = val.unicode()[i];
- ushort uc = c.unicode();
- if (uc < 32) {
- if (!escaping) {
- escaping = true;
- ret += QLatin1String("$$escape_expand(");
- }
- switch (uc) {
- case '\r':
- ret += QLatin1String("\\\\r");
- break;
- case '\n':
- ret += QLatin1String("\\\\n");
- break;
- case '\t':
- ret += QLatin1String("\\\\t");
- break;
- default:
- ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
- break;
- }
- } else {
- if (escaping) {
- escaping = false;
- ret += QLatin1Char(')');
- }
- switch (uc) {
- case '\\':
- ret += QLatin1String("\\\\");
- break;
- case '"':
- ret += QLatin1String("\\\"");
- break;
- case '\'':
- ret += QLatin1String("\\'");
- break;
- case '$':
- ret += QLatin1String("\\$");
- break;
- case '#':
- ret += QLatin1String("$${LITERAL_HASH}");
- break;
- case 32:
- quote = true;
- // fallthrough
- default:
- ret += c;
- break;
- }
- }
- }
- if (escaping)
- ret += QLatin1Char(')');
- if (quote) {
- ret.prepend(QLatin1Char('"'));
- ret.append(QLatin1Char('"'));
- }
- return ret;
-}
-
-static bool
-writeFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr)
-{
- QByteArray bytes = contents.toLocal8Bit();
- QFile cfile(name);
- if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- if (cfile.readAll() == bytes)
- return true;
- cfile.close();
- }
- if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
- *errStr = cfile.errorString();
- return false;
- }
- cfile.write(bytes);
- cfile.close();
- if (cfile.error() != QFile::NoError) {
- *errStr = cfile.errorString();
- return false;
- }
- return true;
-}
-
-static QByteArray
-getCommandOutput(const QString &args)
-{
- QByteArray out;
- if (FILE *proc = QT_POPEN(args.toLatin1().constData(), "r")) {
- while (!feof(proc)) {
- char buff[10 * 1024];
- int read_in = int(fread(buff, 1, sizeof(buff), proc));
- if (!read_in)
- break;
- out += QByteArray(buff, read_in);
- }
- QT_PCLOSE(proc);
- }
- return out;
-}
-
-#ifdef Q_OS_WIN
-static QString windowsErrorCode()
-{
- wchar_t *string = 0;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&string,
- 0,
- NULL);
- QString ret = QString::fromWCharArray(string);
- LocalFree((HLOCAL)string);
- return ret;
-}
-#endif
-
-QStringList
-QMakeProject::doProjectExpand(QString func, const QString &params,
- QHash<QString, QStringList> &place)
-{
- return doProjectExpand(func, split_arg_list(params), place);
-}
-
-QStringList
-QMakeProject::doProjectExpand(QString func, QStringList args,
- QHash<QString, QStringList> &place)
-{
- QList<QStringList> args_list;
- for(int i = 0; i < args.size(); ++i) {
- QStringList arg = split_value_list(args[i]), tmp;
- for(int i = 0; i < arg.size(); ++i)
- tmp += doVariableReplaceExpand(arg[i], place);;
- args_list += tmp;
- }
- return doProjectExpand(func, args_list, place);
-}
-
-static void
-populateDeps(const QStringList &deps, const QString &prefix,
- QHash<QString, QSet<QString> > &dependencies, QHash<QString, QStringList> &dependees,
- QStringList &rootSet, QHash<QString, QStringList> &place)
-{
- foreach (const QString &item, deps)
- if (!dependencies.contains(item)) {
- QSet<QString> &dset = dependencies[item]; // Always create entry
- QStringList depends = place.value(prefix + item + ".depends");
- if (depends.isEmpty()) {
- rootSet << item;
- } else {
- foreach (const QString &dep, depends) {
- dset.insert(dep);
- dependees[dep] << item;
- }
- populateDeps(depends, prefix, dependencies, dependees, rootSet, place);
- }
- }
-}
-
-QStringList
-QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
- QHash<QString, QStringList> &place)
-{
- func = func.trimmed();
- if(replaceFunctions.contains(func)) {
- FunctionBlock *defined = replaceFunctions[func];
- function_blocks.push(defined);
- QStringList ret;
- defined->exec(args_list, this, place, ret);
- bool correct = function_blocks.pop() == defined;
- Q_ASSERT(correct); Q_UNUSED(correct);
- return ret;
- }
-
- QStringList args; //why don't the builtin functions just use args_list? --Sam
- for(int i = 0; i < args_list.size(); ++i)
- args += args_list[i].join(QString(Option::field_sep));
-
- ExpandFunc func_t = qmake_expandFunctions().value(func);
- if (!func_t && (func_t = qmake_expandFunctions().value(func.toLower())))
- warn_msg(WarnDeprecated, "%s:%d: Using uppercased builtin functions is deprecated.",
- parser.file.toLatin1().constData(), parser.line_no);
- debug_msg(1, "Running project expand: %s(%s) [%d]",
- func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
-
- QStringList ret;
- switch(func_t) {
- case E_MEMBER: {
- if(args.count() < 1 || args.count() > 3) {
- fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- bool ok = true;
- const QStringList &var = values(args.first(), place);
- int start = 0, end = 0;
- if(args.count() >= 2) {
- QString start_str = args[1];
- start = start_str.toInt(&ok);
- if(!ok) {
- if(args.count() == 2) {
- int dotdot = start_str.indexOf("..");
- if(dotdot != -1) {
- start = start_str.left(dotdot).toInt(&ok);
- if(ok)
- end = start_str.mid(dotdot+2).toInt(&ok);
- }
- }
- if(!ok)
- fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- start_str.toLatin1().constData());
- } else {
- end = start;
- if(args.count() == 3)
- end = args[2].toInt(&ok);
- if(!ok)
- fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args[2].toLatin1().constData());
- }
- }
- if(ok) {
- if(start < 0)
- start += var.count();
- if(end < 0)
- end += var.count();
- if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
- //nothing
- } else if(start < end) {
- for(int i = start; i <= end && (int)var.count() >= i; i++)
- ret += var[i];
- } else {
- for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--)
- ret += var[i];
- }
- }
- }
- break; }
- case E_FIRST:
- case E_LAST: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: %s(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
- } else {
- const QStringList &var = values(args.first(), place);
- if(!var.isEmpty()) {
- if(func_t == E_FIRST)
- ret = QStringList(var[0]);
- else
- ret = QStringList(var[var.size()-1]);
- }
- }
- break; }
- case E_CAT: {
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d: cat(file) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString file = Option::normalizePath(args[0]);
-
- bool blob = false;
- bool lines = false;
- bool singleLine = true;
- if (args.count() > 1) {
- if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
- singleLine = false;
- else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
- blob = true;
- else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
- lines = true;
- }
- QFile qfile(file);
- if(qfile.open(QIODevice::ReadOnly)) {
- QTextStream stream(&qfile);
- if (blob) {
- ret += stream.readAll();
- } else {
- while (!stream.atEnd()) {
- if (lines) {
- ret += stream.readLine();
- } else {
- ret += split_value_list(stream.readLine().trimmed());
- if (!singleLine)
- ret += "\n";
- }
- }
- }
- }
- }
- break; }
- case E_FROMFILE: {
- if(args.count() != 2) {
- fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString seek_var = args[1], file = Option::normalizePath(args[0]);
-
- QHash<QString, QStringList> tmp;
- if(doProjectInclude(file, IncludeFlagNewParser, tmp) == IncludeSuccess) {
- if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
- QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
- const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
- for(int i = 0; i < in.size(); ++i) {
- if(out.indexOf(in[i]) == -1)
- out += in[i];
- }
- }
- ret = tmp[seek_var];
- }
- }
- break; }
- case E_EVAL: {
- if (args.count() != 1) {
- fprintf(stderr, "%s:%d: eval(variable) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
-
- } else {
- ret += place.value(args.at(0));
- }
- break; }
- case E_LIST: {
- static int x = 0;
- QString tmp;
- tmp.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++);
- ret = QStringList(tmp);
- QStringList &lst = (*((QHash<QString, QStringList>*)&place))[tmp];
- lst.clear();
- for(QStringList::ConstIterator arg_it = args.begin();
- arg_it != args.end(); ++arg_it)
- lst += split_value_list((*arg_it));
- break; }
- case E_SPRINTF: {
- if(args.count() < 1) {
- fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString tmp = args.at(0);
- for(int i = 1; i < args.count(); ++i)
- tmp = tmp.arg(args.at(i));
- ret = split_value_list(tmp);
- }
- break; }
- case E_FORMAT_NUMBER:
- if (args.count() > 2) {
- fprintf(stderr, "%s:%d: format_number(number[, options...]) requires one or two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- int ibase = 10;
- int obase = 10;
- int width = 0;
- bool zeropad = false;
- bool leftalign = false;
- enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
- if (args.count() >= 2) {
- foreach (const QString &opt, split_value_list(args.at(1))) {
- if (opt.startsWith(QLatin1String("ibase="))) {
- ibase = opt.mid(6).toInt();
- } else if (opt.startsWith(QLatin1String("obase="))) {
- obase = opt.mid(6).toInt();
- } else if (opt.startsWith(QLatin1String("width="))) {
- width = opt.mid(6).toInt();
- } else if (opt == QLatin1String("zeropad")) {
- zeropad = true;
- } else if (opt == QLatin1String("padsign")) {
- sign = PadSign;
- } else if (opt == QLatin1String("alwayssign")) {
- sign = AlwaysSign;
- } else if (opt == QLatin1String("leftalign")) {
- leftalign = true;
- } else {
- fprintf(stderr, "%s:%d: format_number(): invalid format option %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- opt.toLatin1().constData());
- goto formfail;
- }
- }
- }
- if (args.at(0).contains(QLatin1Char('.'))) {
- fprintf(stderr, "%s:%d: format_number(): floats are currently not supported.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- break;
- }
- bool ok;
- qlonglong num = args.at(0).toLongLong(&ok, ibase);
- if (!ok) {
- fprintf(stderr, "%s:%d: format_number(): malformed number %s for base %d.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(0).toLatin1().constData(), ibase);
- break;
- }
- QString outstr;
- if (num < 0) {
- num = -num;
- outstr = QLatin1Char('-');
- } else if (sign == AlwaysSign) {
- outstr = QLatin1Char('+');
- } else if (sign == PadSign) {
- outstr = QLatin1Char(' ');
- }
- QString numstr = QString::number(num, obase);
- int space = width - outstr.length() - numstr.length();
- if (space <= 0) {
- outstr += numstr;
- } else if (leftalign) {
- outstr += numstr + QString(space, QLatin1Char(' '));
- } else if (zeropad) {
- outstr += QString(space, QLatin1Char('0')) + numstr;
- } else {
- outstr.prepend(QString(space, QLatin1Char(' ')));
- outstr += numstr;
- }
- ret += outstr;
- }
- formfail:
- break;
- case E_JOIN: {
- if(args.count() < 1 || args.count() > 4) {
- fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
- "arguments.\n", parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString glue, before, after;
- if(args.count() >= 2)
- glue = args[1];
- if(args.count() >= 3)
- before = args[2];
- if(args.count() == 4)
- after = args[3];
- const QStringList &var = values(args.first(), place);
- if(!var.isEmpty())
- ret = split_value_list(before + var.join(glue) + after);
- }
- break; }
- case E_SPLIT: {
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d split(var, sep) requires one or two arguments\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString sep = QString(Option::field_sep);
- if(args.count() >= 2)
- sep = args[1];
- QStringList var = values(args.first(), place);
- for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) {
- QStringList lst = (*vit).split(sep);
- for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit)
- ret += (*spltit);
- }
- }
- break; }
- case E_BASENAME:
- case E_DIRNAME:
- case E_SECTION: {
- bool regexp = false;
- QString sep, var;
- int beg=0, end=-1;
- if(func_t == E_SECTION) {
- if(args.count() != 3 && args.count() != 4) {
- fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- var = args[0];
- sep = args[1];
- beg = args[2].toInt();
- if(args.count() == 4)
- end = args[3].toInt();
- }
- } else {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d %s(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
- } else {
- var = args[0];
- regexp = true;
- sep = "[" + QRegExp::escape(Option::dir_sep) + "/]";
- if(func_t == E_DIRNAME)
- end = -2;
- else
- beg = -1;
- }
- }
- if(!var.isNull()) {
- const QStringList &l = values(var, place);
- for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
- QString separator = sep;
- if(regexp)
- ret += (*it).section(QRegExp(separator), beg, end);
- else
- ret += (*it).section(separator, beg, end);
- }
- }
- break; }
- case E_FIND: {
- if(args.count() != 2) {
- fprintf(stderr, "%s:%d find(var, str) requires two arguments\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QRegExp regx(args[1]);
- const QStringList &var = values(args.first(), place);
- for(QStringList::ConstIterator vit = var.begin();
- vit != var.end(); ++vit) {
- if(regx.indexIn(*vit) != -1)
- ret += (*vit);
- }
- }
- break; }
- case E_SYSTEM: {
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d system(execut) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- bool blob = false;
- bool lines = false;
- bool singleLine = true;
- if (args.count() > 1) {
- if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
- singleLine = false;
- else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
- blob = true;
- else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
- lines = true;
- }
- QByteArray bytes = getCommandOutput(args.at(0));
- if (lines) {
- QTextStream stream(bytes);
- while (!stream.atEnd())
- ret += stream.readLine();
- } else {
- QString output = QString::fromLocal8Bit(bytes);
- if (blob) {
- ret += output;
- } else {
- output.replace(QLatin1Char('\t'), QLatin1Char(' '));
- if (singleLine)
- output.replace(QLatin1Char('\n'), QLatin1Char(' '));
- ret += split_value_list(output);
- }
- }
- }
- break; }
- case E_UNIQUE: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d unique(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- const QStringList &var = values(args.first(), place);
- for(int i = 0; i < var.count(); i++) {
- if(!ret.contains(var[i]))
- ret.append(var[i]);
- }
- }
- break; }
- case E_REVERSE:
- if (args.count() != 1) {
- fprintf(stderr, "%s:%d reverse(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QStringList var = values(args.first(), place);
- for (int i = 0; i < var.size() / 2; i++)
- var.swap(i, var.size() - i - 1);
- ret += var;
- }
- break;
- case E_QUOTE:
- ret = args;
- break;
- case E_ESCAPE_EXPAND: {
- for(int i = 0; i < args.size(); ++i) {
- QChar *i_data = args[i].data();
- int i_len = args[i].length();
- for(int x = 0; x < i_len; ++x) {
- if(*(i_data+x) == '\\' && x < i_len-1) {
- if(*(i_data+x+1) == '\\') {
- ++x;
- } else {
- struct {
- char in, out;
- } mapped_quotes[] = {
- { 'n', '\n' },
- { 't', '\t' },
- { 'r', '\r' },
- { 0, 0 }
- };
- for(int i = 0; mapped_quotes[i].in; ++i) {
- if(*(i_data+x+1) == mapped_quotes[i].in) {
- *(i_data+x) = mapped_quotes[i].out;
- if(x < i_len-2)
- memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
- --i_len;
- break;
- }
- }
- }
- }
- }
- ret.append(QString(i_data, i_len));
- }
- break; }
- case E_RE_ESCAPE: {
- for(int i = 0; i < args.size(); ++i)
- ret += QRegExp::escape(args[i]);
- break; }
- case E_VAL_ESCAPE:
- if (args.count() != 1) {
- fprintf(stderr, "%s:%d val_escape(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QStringList vals = values(args.at(0), place);
- ret.reserve(vals.length());
- foreach (const QString &str, vals)
- ret += quoteValue(str);
- }
- break;
- case E_UPPER:
- case E_LOWER: {
- for(int i = 0; i < args.size(); ++i) {
- if(func_t == E_UPPER)
- ret += args[i].toUpper();
- else
- ret += args[i].toLower();
- }
- break; }
- case E_FILES: {
- if(args.count() != 1 && args.count() != 2) {
- fprintf(stderr, "%s:%d files(pattern) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- bool recursive = false;
- if(args.count() == 2)
- recursive = (args[1].toLower() == "true" || args[1].toInt());
- QStringList dirs;
- QString r = Option::normalizePath(args[0]);
- int slash = r.lastIndexOf(QLatin1Char('/'));
- if(slash != -1) {
- dirs.append(r.left(slash));
- r = r.mid(slash+1);
- } else {
- dirs.append("");
- }
-
- QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
- for(int d = 0; d < dirs.count(); d++) {
- QString dir = dirs[d];
- if (!dir.isEmpty() && !dir.endsWith(QLatin1Char('/')))
- dir += "/";
-
- QDir qdir(dir);
- for(int i = 0; i < (int)qdir.count(); ++i) {
- if(qdir[i] == "." || qdir[i] == "..")
- continue;
- QString fname = dir + qdir[i];
- if(QFileInfo(fname).isDir()) {
- if(recursive)
- dirs.append(fname);
- }
- if(regex.exactMatch(qdir[i]))
- ret += fname;
- }
- }
- }
- break; }
- case E_PROMPT: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d prompt(question) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else if (Option::output.fileName() == "-") {
- fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QString msg = fixEnvVariables(args.first());
- if(!msg.endsWith("?"))
- msg += "?";
- fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(),
- msg.toLatin1().constData());
-
- QFile qfile;
- if(qfile.open(stdin, QIODevice::ReadOnly)) {
- QTextStream t(&qfile);
- ret = split_value_list(t.readLine());
- }
- }
- break; }
- case E_REPLACE: {
- if(args.count() != 3 ) {
- fprintf(stderr, "%s:%d replace(var, before, after) requires three arguments\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- const QRegExp before( args[1] );
- const QString after( args[2] );
- QStringList var = values(args.first(), place);
- for(QStringList::Iterator it = var.begin(); it != var.end(); ++it)
- ret += it->replace(before, after);
- }
- break; }
- case E_SIZE: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: size(var) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- int size = values(args[0], place).size();
- ret += QString::number(size);
- }
- break; }
- case E_SORT_DEPENDS:
- case E_RESOLVE_DEPENDS: {
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d: %s(var, prefix) requires one or two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
- } else {
- QHash<QString, QSet<QString> > dependencies;
- QHash<QString, QStringList> dependees;
- QStringList rootSet;
-
- QStringList orgList = values(args[0], place);
- populateDeps(orgList, (args.count() != 2 ? QString() : args[1]),
- dependencies, dependees, rootSet, place);
-
- for (int i = 0; i < rootSet.size(); ++i) {
- const QString &item = rootSet.at(i);
- if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
- ret.prepend(item);
- foreach (const QString &dep, dependees[item]) {
- QSet<QString> &dset = dependencies[dep];
- dset.remove(rootSet.at(i)); // *Don't* use 'item' - rootSet may have changed!
- if (dset.isEmpty())
- rootSet << dep;
- }
- }
- }
- break; }
- case E_ENUMERATE_VARS:
- ret += place.keys();
- break;
- case E_SHADOWED: {
- QString val = QDir::cleanPath(QFileInfo(args.at(0)).absoluteFilePath());
- if (Option::mkfile::source_root.isEmpty()) {
- ret += val;
- } else if (val.startsWith(Option::mkfile::source_root)
- && (val.length() == Option::mkfile::source_root.length()
- || val.at(Option::mkfile::source_root.length()) == QLatin1Char('/'))) {
- ret += Option::mkfile::build_root + val.mid(Option::mkfile::source_root.length());
- }
- break; }
- case E_ABSOLUTE_PATH:
- if (args.count() > 2)
- fprintf(stderr, "%s:%d absolute_path(path[, base]) requires one or two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else
- ret += QDir::cleanPath(QDir(args.count() > 1 ? args.at(1) : QString())
- .absoluteFilePath(args.at(0)));
- break;
- case E_RELATIVE_PATH:
- if (args.count() > 2) {
- fprintf(stderr, "%s:%d relative_path(path[, base]) requires one or two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- QDir baseDir(args.count() > 1 ? args.at(1) : QString());
- ret += baseDir.relativeFilePath(baseDir.absoluteFilePath(args.at(0)));
- }
- break;
- case E_CLEAN_PATH:
- if (args.count() != 1)
- fprintf(stderr, "%s:%d clean_path(path) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else
- ret += QDir::cleanPath(args.at(0));
- break;
- case E_SYSTEM_PATH:
- if (args.count() != 1)
- fprintf(stderr, "%s:%d system_path(path) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else
- ret += Option::fixPathToLocalOS(args.at(0), false);
- break;
- case E_SHELL_PATH:
- if (args.count() != 1)
- fprintf(stderr, "%s:%d shell_path(path) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else
- ret += Option::fixPathToTargetOS(args.at(0), false);
- break;
- case E_SYSTEM_QUOTE:
- if (args.count() != 1)
- fprintf(stderr, "%s:%d system_quote(args) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else
-#ifdef Q_OS_WIN
- ret += shellQuoteWin(args.at(0));
-#else
- ret += shellQuoteUnix(args.at(0));
-#endif
- break;
- case E_SHELL_QUOTE:
- if (args.count() != 1)
- fprintf(stderr, "%s:%d shell_quote(args) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- else if (Option::dir_sep.at(0) != QLatin1Char('/'))
- ret += shellQuoteWin(args.at(0));
- else
- ret += shellQuoteUnix(args.at(0));
- break;
- default: {
- fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
- parser.file.toLatin1().constData(), parser.line_no,
- func.toLatin1().constData());
- break; }
- }
- return ret;
-}
-
-bool
-QMakeProject::doProjectTest(QString func, QStringList args, QHash<QString, QStringList> &place)
-{
- QList<QStringList> args_list;
- for(int i = 0; i < args.size(); ++i) {
- QStringList arg = split_value_list(args[i]), tmp;
- for(int i = 0; i < arg.size(); ++i)
- tmp += doVariableReplaceExpand(arg[i], place);
- args_list += tmp;
- }
- return doProjectTest(func, args_list, place);
-}
-
-bool
-QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QString, QStringList> &place)
-{
- func = func.trimmed();
-
- if(testFunctions.contains(func)) {
- FunctionBlock *defined = testFunctions[func];
- QStringList ret;
- function_blocks.push(defined);
- defined->exec(args_list, this, place, ret);
- bool correct = function_blocks.pop() == defined;
- Q_ASSERT(correct); Q_UNUSED(correct);
-
- if(ret.isEmpty()) {
- return true;
- } else {
- if(ret.first() == "true") {
- return true;
- } else if(ret.first() == "false") {
- return false;
- } else {
- bool ok;
- int val = ret.first().toInt(&ok);
- if(ok)
- return val;
- fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n",
- parser.file.toLatin1().constData(),
- parser.line_no, func.toLatin1().constData(),
- ret.join("::").toLatin1().constData());
- }
- return false;
- }
- return false;
- }
-
- QStringList args; //why don't the builtin functions just use args_list? --Sam
- for(int i = 0; i < args_list.size(); ++i)
- args += args_list[i].join(QString(Option::field_sep));
-
- TestFunc func_t = qmake_testFunctions().value(func);
- debug_msg(1, "Running project test: %s(%s) [%d]",
- func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
-
- switch(func_t) {
- case T_REQUIRES:
- return doProjectCheckReqs(args, place);
- case T_LESSTHAN:
- case T_GREATERTHAN: {
- if(args.count() != 2) {
- fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
- parser.line_no, func.toLatin1().constData());
- return false;
- }
- QString rhs(args[1]), lhs(values(args[0], place).join(QString(Option::field_sep)));
- bool ok;
- int rhs_int = rhs.toInt(&ok);
- if(ok) { // do integer compare
- int lhs_int = lhs.toInt(&ok);
- if(ok) {
- if(func_t == T_GREATERTHAN)
- return lhs_int > rhs_int;
- return lhs_int < rhs_int;
- }
- }
- if(func_t == T_GREATERTHAN)
- return lhs > rhs;
- return lhs < rhs; }
- case T_IF: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: if(condition) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- const QString cond = args.first();
- const QChar *d = cond.unicode();
- QChar quote = 0;
- bool ret = true, or_op = false;
- QString test;
- for(int d_off = 0, parens = 0, d_len = cond.size(); d_off < d_len; ++d_off) {
- if(!quote.isNull()) {
- if(*(d+d_off) == quote)
- quote = QChar();
- } else if(*(d+d_off) == '(') {
- ++parens;
- } else if(*(d+d_off) == ')') {
- --parens;
- } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) {
- quote = *(d+d_off);
- }
- if(!parens && quote.isNull() && (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('|') || d_off == d_len-1)) {
- if(d_off == d_len-1)
- test += *(d+d_off);
- if(!test.isEmpty()) {
- if (or_op != ret)
- ret = doProjectTest(test, place);
- test.clear();
- }
- if(*(d+d_off) == QLatin1Char(':')) {
- or_op = false;
- } else if(*(d+d_off) == QLatin1Char('|')) {
- or_op = true;
- }
- } else {
- test += *(d+d_off);
- }
- }
- return ret; }
- case T_EQUALS:
- if(args.count() != 2) {
- fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
- parser.line_no, func.toLatin1().constData());
- return false;
- }
- return values(args[0], place).join(QString(Option::field_sep)) == args[1];
- case T_EXISTS: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- QString file = Option::normalizePath(args.first());
-
- if(QFile::exists(file))
- return true;
- //regular expression I guess
- QString dirstr = qmake_getpwd();
- int slsh = file.lastIndexOf(QLatin1Char('/'));
- if(slsh != -1) {
- dirstr = file.left(slsh+1);
- file = file.right(file.length() - slsh - 1);
- }
- return QDir(dirstr).entryList(QStringList(file)).count(); }
- case T_EXPORT:
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- for(int i = 0; i < function_blocks.size(); ++i) {
- FunctionBlock *f = function_blocks.at(i);
- f->vars[args[0]] = values(args[0], place);
- if(!i && f->calling_place)
- (*f->calling_place)[args[0]] = values(args[0], place);
- }
- return true;
- case T_CLEAR:
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- if(!place.contains(args[0]))
- return false;
- place[args[0]].clear();
- return true;
- case T_UNSET:
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- if(!place.contains(args[0]))
- return false;
- place.remove(args[0]);
- return true;
- case T_EVAL: {
- if(args.count() < 1 && 0) {
- fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- QString project = args.join(" ");
- parser_info pi = parser;
- parser.from_file = false;
- parser.file = "(eval)";
- parser.line_no = 0;
- QTextStream t(&project, QIODevice::ReadOnly);
- bool ret = read(t, place);
- parser = pi;
- return ret; }
- case T_CONFIG: {
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- if(args.count() == 1)
- return isActiveConfig(args[0]);
- const QStringList mutuals = args[1].split('|');
- const QStringList &configs = values("CONFIG", place);
- for(int i = configs.size()-1; i >= 0; i--) {
- for(int mut = 0; mut < mutuals.count(); mut++) {
- if(configs[i] == mutuals[mut].trimmed())
- return (configs[i] == args[0]);
- }
- }
- return false; }
- case T_SYSTEM:
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- if(args.count() == 2) {
- const QString sarg = args[1];
- if (sarg.toLower() == "true" || sarg.toInt())
- warn_msg(WarnParser, "%s:%d: system()'s second argument is now hard-wired to false.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- }
- return system(args[0].toLatin1().constData()) == 0;
- case T_RETURN:
- if(function_blocks.isEmpty()) {
- fprintf(stderr, "%s:%d unexpected return()\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- FunctionBlock *f = function_blocks.top();
- f->cause_return = true;
- if(args_list.count() >= 1)
- f->return_value += args_list[0];
- }
- return true;
- case T_BREAK:
- if(iterator)
- iterator->cause_break = true;
- else
- fprintf(stderr, "%s:%d unexpected break()\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return true;
- case T_NEXT:
- if(iterator)
- iterator->cause_next = true;
- else
- fprintf(stderr, "%s:%d unexpected next()\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return true;
- case T_DEFINED:
- if(args.count() < 1 || args.count() > 2) {
- fprintf(stderr, "%s:%d: defined(function) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- } else {
- if(args.count() > 1) {
- if(args[1] == "test")
- return testFunctions.contains(args[0]);
- else if(args[1] == "replace")
- return replaceFunctions.contains(args[0]);
- else if(args[1] == "var")
- return place.contains(args[0]);
- fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args[1].toLatin1().constData());
- } else {
- if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0]))
- return true;
- }
- }
- return false;
- case T_CONTAINS: {
- if(args.count() < 2 || args.count() > 3) {
- fprintf(stderr, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- QRegExp regx(args[1]);
- const QStringList &l = values(args[0], place);
- if(args.count() == 2) {
- for(int i = 0; i < l.size(); ++i) {
- const QString val = l[i];
- if(regx.exactMatch(val) || val == args[1])
- return true;
- }
- } else {
- const QStringList mutuals = args[2].split('|');
- for(int i = l.size()-1; i >= 0; i--) {
- const QString val = l[i];
- for(int mut = 0; mut < mutuals.count(); mut++) {
- if(val == mutuals[mut].trimmed())
- return (regx.exactMatch(val) || val == args[1]);
- }
- }
- }
- return false; }
- case T_INFILE: {
- if(args.count() < 2 || args.count() > 3) {
- fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
-
- bool ret = false;
- QHash<QString, QStringList> tmp;
- if(doProjectInclude(Option::normalizePath(args[0]), IncludeFlagNewParser, tmp) == IncludeSuccess) {
- if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
- QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
- const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
- for(int i = 0; i < in.size(); ++i) {
- if(out.indexOf(in[i]) == -1)
- out += in[i];
- }
- }
- if(args.count() == 2) {
- ret = tmp.contains(args[1]);
- } else {
- QRegExp regx(args[2]);
- const QStringList &l = tmp[args[1]];
- for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
- if(regx.exactMatch((*it)) || (*it) == args[2]) {
- ret = true;
- break;
- }
- }
- }
- }
- return ret; }
- case T_COUNT:
- if(args.count() != 2 && args.count() != 3) {
- fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- if(args.count() == 3) {
- QString comp = args[2];
- if(comp == ">" || comp == "greaterThan")
- return values(args[0], place).count() > args[1].toInt();
- if(comp == ">=")
- return values(args[0], place).count() >= args[1].toInt();
- if(comp == "<" || comp == "lessThan")
- return values(args[0], place).count() < args[1].toInt();
- if(comp == "<=")
- return values(args[0], place).count() <= args[1].toInt();
- if(comp == "equals" || comp == "isEqual" || comp == "=" || comp == "==")
- return values(args[0], place).count() == args[1].toInt();
- fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(),
- parser.line_no, comp.toLatin1().constData());
- return false;
- }
- return values(args[0], place).count() == args[1].toInt();
- case T_ISEMPTY:
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- return values(args[0], place).isEmpty();
- case T_INCLUDE:
- case T_LOAD: {
- QString parseInto;
- const bool include_statement = (func_t == T_INCLUDE);
- bool ignore_error = false;
- if(args.count() >= 2) {
- if(func_t == T_INCLUDE) {
- parseInto = args[1];
- if (args.count() == 3){
- QString sarg = args[2];
- if (sarg.toLower() == "true" || sarg.toInt())
- ignore_error = true;
- }
- } else {
- QString sarg = args[1];
- ignore_error = (sarg.toLower() == "true" || sarg.toInt());
- }
- } else if(args.count() != 1) {
- QString func_desc = "load(feature)";
- if(include_statement)
- func_desc = "include(file)";
- fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no, func_desc.toLatin1().constData());
- return false;
- }
- QString file = Option::normalizePath(args.first());
- uchar flags = IncludeFlagNone;
- if(!include_statement)
- flags |= IncludeFlagFeature;
- IncludeStatus stat = IncludeFailure;
- if(!parseInto.isEmpty()) {
- QHash<QString, QStringList> symbols;
- stat = doProjectInclude(file, flags|IncludeFlagNewProject, symbols);
- if(stat == IncludeSuccess) {
- QHash<QString, QStringList> out_place;
- for(QHash<QString, QStringList>::ConstIterator it = place.begin(); it != place.end(); ++it) {
- const QString var = it.key();
- if(var != parseInto && !var.startsWith(parseInto + "."))
- out_place.insert(var, it.value());
- }
- for(QHash<QString, QStringList>::ConstIterator it = symbols.begin(); it != symbols.end(); ++it) {
- const QString var = it.key();
- if(!var.startsWith("."))
- out_place.insert(parseInto + "." + it.key(), it.value());
- }
- place = out_place;
- }
- } else {
- stat = doProjectInclude(file, flags, place);
- }
- if(stat == IncludeFeatureAlreadyLoaded) {
- warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s",
- parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
- } else if(stat == IncludeNoExist && !ignore_error) {
- warn_msg(WarnAll, "%s:%d: Unable to find file for inclusion %s",
- parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
- return false;
- } else if(stat >= IncludeFailure) {
- if(!ignore_error) {
- printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData());
- if (!ignore_error)
-#if defined(QT_BUILD_QMAKE_LIBRARY)
- return false;
-#else
- exit(3);
-#endif
- }
- return false;
- }
- return true; }
- case T_DEBUG: {
- if(args.count() != 2) {
- fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no);
- return false;
- }
- QString msg = fixEnvVariables(args[1]);
- debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData());
- return true; }
- case T_LOG:
- case T_ERROR:
- case T_MESSAGE:
- case T_WARNING: {
- if(args.count() != 1) {
- fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(),
- parser.line_no, func.toLatin1().constData());
- return false;
- }
- QString msg = fixEnvVariables(args.first());
- if (func_t == T_LOG) {
- fputs(msg.toLatin1().constData(), stderr);
- } else {
- fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
- if (func == "error")
-#if defined(QT_BUILD_QMAKE_LIBRARY)
- return false;
-#else
- exit(2);
-#endif
- }
- return true; }
- case T_OPTION:
- if (args.count() != 1) {
- fprintf(stderr, "%s:%d: option() requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- if (args.first() == "host_build") {
- if (!host_build && isActiveConfig("cross_compile")) {
- host_build = true;
- need_restart = true;
- }
- } else {
- fprintf(stderr, "%s:%d: unrecognized option() argument '%s'.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.first().toLatin1().constData());
- return false;
- }
- return true;
- case T_CACHE: {
- if (args.count() > 3) {
- fprintf(stderr, "%s:%d: cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- bool persist = true;
- bool super = false;
- enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
- QString srcvar;
- if (args.count() >= 2) {
- foreach (const QString &opt, split_value_list(args.at(1))) {
- if (opt == QLatin1String("transient")) {
- persist = false;
- } else if (opt == QLatin1String("super")) {
- super = true;
- } else if (opt == QLatin1String("set")) {
- mode = CacheSet;
- } else if (opt == QLatin1String("add")) {
- mode = CacheAdd;
- } else if (opt == QLatin1String("sub")) {
- mode = CacheSub;
- } else {
- fprintf(stderr, "%s:%d: cache(): invalid flag %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- opt.toLatin1().constData());
- return false;
- }
- }
- if (args.count() >= 3) {
- srcvar = args.at(2);
- } else if (mode != CacheSet) {
- fprintf(stderr, "%s:%d: cache(): modes other than 'set' require a source variable.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- }
- QString varstr;
- QString dstvar = args.at(0);
- if (!dstvar.isEmpty()) {
- if (srcvar.isEmpty())
- srcvar = dstvar;
- if (!place.contains(srcvar)) {
- fprintf(stderr, "%s:%d: variable %s is not defined.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- srcvar.toLatin1().constData());
- return false;
- }
- // The current ("native") value can differ from the cached value, e.g., the current
- // CONFIG will typically have more values than the cached one. Therefore we deal with
- // them separately.
- const QStringList diffval = values(srcvar, place);
- const QStringList oldval = base_vars.value(dstvar);
- QStringList newval;
- if (mode == CacheSet) {
- newval = diffval;
- } else {
- newval = oldval;
- if (mode == CacheAdd)
- newval += diffval;
- else
- subAll(&newval, diffval);
- }
- // We assume that whatever got the cached value to be what it is now will do so
- // the next time as well, so it is OK that the early exit here will skip the
- // persisting as well.
- if (oldval == newval)
- return true;
- base_vars[dstvar] = newval;
- do {
- if (dstvar == "QMAKEPATH")
- cached_qmakepath = newval;
- else if (dstvar == "QMAKEFEATURES")
- cached_qmakefeatures = newval;
- else
- break;
- invalidateFeatureRoots();
- } while (false);
- if (!persist)
- return true;
- varstr = dstvar;
- if (mode == CacheAdd)
- varstr += QLatin1String(" +=");
- else if (mode == CacheSub)
- varstr += QLatin1String(" -=");
- else
- varstr += QLatin1String(" =");
- if (diffval.count() == 1) {
- varstr += QLatin1Char(' ');
- varstr += quoteValue(diffval.at(0));
- } else if (!diffval.isEmpty()) {
- foreach (const QString &vval, diffval) {
- varstr += QLatin1String(" \\\n ");
- varstr += quoteValue(vval);
- }
- }
- varstr += QLatin1Char('\n');
- }
- QString fn;
- if (super) {
- if (superfile.isEmpty()) {
- superfile = Option::output_dir + QLatin1String("/.qmake.super");
- printf("Info: creating super cache file %s\n", superfile.toLatin1().constData());
- vars["_QMAKE_SUPER_CACHE_"] << superfile;
- }
- fn = superfile;
- } else {
- if (cachefile.isEmpty()) {
- cachefile = Option::output_dir + QLatin1String("/.qmake.cache");
- printf("Info: creating cache file %s\n", cachefile.toLatin1().constData());
- vars["_QMAKE_CACHE_"] << cachefile;
- if (cached_build_root.isEmpty()) {
- cached_build_root = Option::output_dir;
- cached_source_root = values("_PRO_FILE_PWD_", place).first();
- if (cached_source_root == cached_build_root)
- cached_source_root.clear();
- invalidateFeatureRoots();
- }
- }
- fn = cachefile;
- }
- QFileInfo qfi(fn);
- if (!QDir::current().mkpath(qfi.path())) {
- fprintf(stderr, "%s:%d: ERROR creating cache directory %s\n",
- parser.file.toLatin1().constData(), parser.line_no,
- qfi.path().toLatin1().constData());
- return false;
- }
- QString errStr;
- if (!writeFile(fn, QIODevice::Append, varstr, &errStr)) {
- fprintf(stderr, "ERROR writing cache file %s: %s\n",
- fn.toLatin1().constData(), errStr.toLatin1().constData());
-#if defined(QT_BUILD_QMAKE_LIBRARY)
- return false;
-#else
- exit(2);
-#endif
- }
- return true; }
- case T_MKPATH:
- if (args.count() != 1) {
- fprintf(stderr, "%s:%d: mkpath(name) requires one argument.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- if (!QDir::current().mkpath(args.at(0))) {
- fprintf(stderr, "%s:%d: ERROR creating directory %s\n",
- parser.file.toLatin1().constData(), parser.line_no,
- QDir::toNativeSeparators(args.at(0)).toLatin1().constData());
- return false;
- }
- return true;
- case T_WRITE_FILE: {
- if (args.count() > 3) {
- fprintf(stderr, "%s:%d: write_file(name, [content var, [append]]) requires one to three arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
- QIODevice::OpenMode mode = QIODevice::Truncate;
- QString contents;
- if (args.count() >= 2) {
- QStringList vals = values(args.at(1), place);
- if (!vals.isEmpty())
- contents = vals.join(QLatin1String("\n")) + QLatin1Char('\n');
- if (args.count() >= 3)
- if (!args.at(2).compare(QLatin1String("append"), Qt::CaseInsensitive))
- mode = QIODevice::Append;
- }
- QFileInfo qfi(args.at(0));
- if (!QDir::current().mkpath(qfi.path())) {
- fprintf(stderr, "%s:%d: ERROR creating directory %s\n",
- parser.file.toLatin1().constData(), parser.line_no,
- qfi.path().toLatin1().constData());
- return false;
- }
- QString errStr;
- if (!writeFile(args.at(0), mode, contents, &errStr)) {
- fprintf(stderr, "%s:%d ERROR writing %s: %s\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(0).toLatin1().constData(), errStr.toLatin1().constData());
- return false;
- }
- return true; }
- case T_TOUCH: {
- if (args.count() != 2) {
- fprintf(stderr, "%s:%d: touch(file, reffile) requires two arguments.\n",
- parser.file.toLatin1().constData(), parser.line_no);
- return false;
- }
-#ifdef Q_OS_UNIX
- struct stat st;
- if (stat(args.at(1).toLocal8Bit().constData(), &st)) {
- fprintf(stderr, "%s:%d: ERROR: cannot stat() reference file %s: %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(1).toLatin1().constData(), strerror(errno));
- return false;
- }
- struct utimbuf utb;
- utb.actime = time(0);
- utb.modtime = st.st_mtime;
- if (utime(args.at(0).toLocal8Bit().constData(), &utb)) {
- fprintf(stderr, "%s:%d: ERROR: cannot touch %s: %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(0).toLatin1().constData(), strerror(errno));
- return false;
- }
-#else
- HANDLE rHand = CreateFile((wchar_t*)args.at(1).utf16(),
- GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (rHand == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "%s:%d: ERROR: cannot open() reference file %s: %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(1).toLatin1().constData(),
- windowsErrorCode().toLatin1().constData());
- return false;
- }
- FILETIME ft;
- GetFileTime(rHand, 0, 0, &ft);
- CloseHandle(rHand);
- HANDLE wHand = CreateFile((wchar_t*)args.at(0).utf16(),
- GENERIC_WRITE, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (wHand == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "%s:%d: ERROR: cannot open %s: %s.\n",
- parser.file.toLatin1().constData(), parser.line_no,
- args.at(0).toLatin1().constData(),
- windowsErrorCode().toLatin1().constData());
- return false;
- }
- SetFileTime(wHand, 0, 0, &ft);
- CloseHandle(wHand);
-#endif
- break; }
- default:
- fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no,
- func.toLatin1().constData());
- }
- return false;
-}
-
-bool
-QMakeProject::doProjectCheckReqs(const QStringList &deps, QHash<QString, QStringList> &place)
-{
- bool ret = false;
- for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) {
- bool test = doProjectTest((*it), place);
- if(!test) {
- debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
- parser.file.toLatin1().constData(), parser.line_no,
- (*it).toLatin1().constData());
- place["QMAKE_FAILED_REQUIREMENTS"].append((*it));
- ret = false;
- }
- }
- return ret;
-}
-
-bool
-QMakeProject::test(const QString &v)
-{
- QHash<QString, QStringList> tmp = vars;
- return doProjectTest(v, tmp);
-}
-
-bool
-QMakeProject::test(const ProKey &func, const QList<ProStringList> &args)
-{
- QHash<QString, QStringList> tmp = vars;
- return doProjectTest(func.toQString(), *(const QList<QStringList> *)&args, tmp);
-}
-
-QStringList
-QMakeProject::expand(const QString &str)
-{
- bool ok;
- QHash<QString, QStringList> tmp = vars;
- const QStringList ret = doVariableReplaceExpand(str, tmp, &ok);
- if(ok)
- return ret;
- return QStringList();
-}
-
-QString
-QMakeProject::expand(const QString &str, const QString &file, int line)
-{
- bool ok;
- parser_info pi = parser;
- parser.file = file;
- parser.line_no = line;
- parser.from_file = false;
- QHash<QString, QStringList> tmp = vars;
- const QStringList ret = doVariableReplaceExpand(str, tmp, &ok);
- parser = pi;
- return ok ? ret.join(QString(Option::field_sep)) : QString();
-}
-
-QStringList
-QMakeProject::expand(const ProKey &func, const QList<ProStringList> &args)
-{
- QHash<QString, QStringList> tmp = vars;
- return doProjectExpand(func.toString().toQString(), *(const QList<QStringList> *)&args, tmp);
-}
-
-bool
-QMakeProject::doVariableReplace(QString &str, QHash<QString, QStringList> &place)
-{
- bool ret;
- str = doVariableReplaceExpand(str, place, &ret).join(QString(Option::field_sep));
- return ret;
-}
-
-QStringList
-QMakeProject::doVariableReplaceExpand(const QString &str, QHash<QString, QStringList> &place, bool *ok)
-{
- QStringList ret;
- if(ok)
- *ok = true;
- if(str.isEmpty())
- return ret;
-
- const ushort LSQUARE = '[';
- const ushort RSQUARE = ']';
- const ushort LCURLY = '{';
- const ushort RCURLY = '}';
- const ushort LPAREN = '(';
- const ushort RPAREN = ')';
- const ushort DOLLAR = '$';
- const ushort SLASH = '\\';
- const ushort UNDERSCORE = '_';
- const ushort DOT = '.';
- const ushort SPACE = ' ';
- const ushort TAB = '\t';
- const ushort SINGLEQUOTE = '\'';
- const ushort DOUBLEQUOTE = '"';
-
- ushort unicode, quote = 0;
- const QChar *str_data = str.data();
- const int str_len = str.length();
-
- ushort term;
- QString var, args;
-
- int replaced = 0;
- QString current;
- for(int i = 0; i < str_len; ++i) {
- unicode = str_data[i].unicode();
- const int start_var = i;
- if(unicode == DOLLAR && str_len > i+2) {
- unicode = str_data[++i].unicode();
- if(unicode == DOLLAR) {
- term = 0;
- var.clear();
- args.clear();
- enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
- unicode = str_data[++i].unicode();
- if(unicode == LSQUARE) {
- unicode = str_data[++i].unicode();
- term = RSQUARE;
- var_type = PROPERTY;
- } else if(unicode == LCURLY) {
- unicode = str_data[++i].unicode();
- var_type = VAR;
- term = RCURLY;
- } else if(unicode == LPAREN) {
- unicode = str_data[++i].unicode();
- var_type = ENVIRON;
- term = RPAREN;
- }
- while(1) {
- if(!(unicode & (0xFF<<8)) &&
- unicode != DOT && unicode != UNDERSCORE &&
- //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
- (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
- (unicode < '0' || unicode > '9') && (!term || unicode != '/'))
- break;
- var.append(QChar(unicode));
- if(++i == str_len)
- break;
- unicode = str_data[i].unicode();
- // at this point, i points to either the 'term' or 'next' character (which is in unicode)
- }
- if(var_type == VAR && unicode == LPAREN) {
- var_type = FUNCTION;
- int depth = 0;
- while(1) {
- if(++i == str_len)
- break;
- unicode = str_data[i].unicode();
- if(unicode == LPAREN) {
- depth++;
- } else if(unicode == RPAREN) {
- if(!depth)
- break;
- --depth;
- }
- args.append(QChar(unicode));
- }
- if(++i < str_len)
- unicode = str_data[i].unicode();
- else
- unicode = 0;
- // at this point i is pointing to the 'next' character (which is in unicode)
- // this might actually be a term character since you can do $${func()}
- }
- if(term) {
- if(unicode != term) {
- qmake_error_msg("Missing " + QString(term) + " terminator [found " + (unicode?QString(unicode):QString("end-of-line")) + "]");
- if(ok)
- *ok = false;
- return QStringList();
- }
- } else {
- // move the 'cursor' back to the last char of the thing we were looking at
- --i;
- }
- // since i never points to the 'next' character, there is no reason for this to be set
- unicode = 0;
-
- QStringList replacement;
- if(var_type == ENVIRON) {
- replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
- } else if(var_type == PROPERTY) {
- if (var == "QMAKE_MKSPECS")
- replacement = split_value_list(qmake_mkspec_paths().join(Option::dirlist_sep));
- else if (prop)
- replacement = split_value_list(prop->value(ProKey(var)).toQString());
- } else if(var_type == FUNCTION) {
- replacement = doProjectExpand(var, args, place);
- } else if(var_type == VAR) {
- replacement = magicValues(var, place);
- }
- if(!(replaced++) && start_var)
- current = str.left(start_var);
- if(!replacement.isEmpty()) {
- if(quote) {
- current += replacement.join(QString(Option::field_sep));
- } else {
- current += replacement.takeFirst();
- if(!replacement.isEmpty()) {
- if(!current.isEmpty())
- ret.append(current);
- current = replacement.takeLast();
- if(!replacement.isEmpty())
- ret += replacement;
- }
- }
- }
- debug_msg(2, "Project Parser [var replace]: %s -> %s",
- str.toLatin1().constData(), var.toLatin1().constData(),
- replacement.join("::").toLatin1().constData());
- } else {
- if(replaced)
- current.append("$");
- }
- }
- if(quote && unicode == quote) {
- unicode = 0;
- quote = 0;
- } else if(unicode == SLASH) {
- bool escape = false;
- const char *symbols = "[]{}()$\\'\"";
- for(const char *s = symbols; *s; ++s) {
- if(str_data[i+1].unicode() == (ushort)*s) {
- i++;
- escape = true;
- if(!(replaced++))
- current = str.left(start_var);
- current.append(str.at(i));
- break;
- }
- }
- if(!escape && !backslashWarned) {
- backslashWarned = true;
- warn_msg(WarnDeprecated, "%s:%d: Unescaped backslashes are deprecated.",
- parser.file.toLatin1().constData(), parser.line_no);
- }
- if(escape || !replaced)
- unicode =0;
- } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
- quote = unicode;
- unicode = 0;
- if(!(replaced++) && i)
- current = str.left(i);
- } else if(!quote && (unicode == SPACE || unicode == TAB)) {
- unicode = 0;
- if(!current.isEmpty()) {
- ret.append(current);
- current.clear();
- }
- }
- if(replaced && unicode)
- current.append(QChar(unicode));
- }
- if(!replaced)
- ret = QStringList(str);
- else if(!current.isEmpty())
- ret.append(current);
- //qDebug() << "REPLACE" << str << ret;
- if (quote)
- warn_msg(WarnDeprecated, "%s:%d: Unmatched quotes are deprecated.",
- parser.file.toLatin1().constData(), parser.line_no);
- return ret;
-}
-
-QStringList QMakeProject::magicValues(const QString &_var, const QHash<QString, QStringList> &place) const
-{
- QString var = varMap(_var);
- if (var == QLatin1String("_LINE_")) { //parser line number
- return QStringList(QString::number(parser.line_no));
- } else if(var == QLatin1String("_FILE_")) { //parser file
- return QStringList(parser.file);
- }
- return place[var];
-}
-
-QStringList &QMakeProject::values(const QString &_var, QHash<QString, QStringList> &place)
-{
- QString var = varMap(_var);
- return place[var];
-}
-
bool QMakeProject::isEmpty(const ProKey &v) const
{
- QHash<QString, QStringList>::ConstIterator it = vars.constFind(v.toQString());
- return it == vars.constEnd() || it->isEmpty();
+ ProValueMap::ConstIterator it = m_valuemapStack.first().constFind(v);
+ return it == m_valuemapStack.first().constEnd() || it->isEmpty();
}
-void
-QMakeProject::dump() const
+void QMakeProject::dump() const
{
QStringList out;
- for (QHash<QString, QStringList>::ConstIterator it = vars.begin(); it != vars.end(); ++it) {
+ for (ProValueMap::ConstIterator it = m_valuemapStack.first().begin();
+ it != m_valuemapStack.first().end(); ++it) {
if (!it.key().startsWith('.')) {
QString str = it.key() + " =";
- foreach (const QString &v, it.value())
- str += ' ' + quoteValue(v);
+ foreach (const ProString &v, it.value())
+ str += ' ' + formatValue(v);
out << str;
}
}
diff --git a/qmake/project.h b/qmake/project.h
index 3d6de87298..efee317386 100644
--- a/qmake/project.h
+++ b/qmake/project.h
@@ -42,165 +42,54 @@
#ifndef PROJECT_H
#define PROJECT_H
-#include <proitems.h>
-
-#include <qstringlist.h>
-#include <qtextstream.h>
-#include <qstring.h>
-#include <qstack.h>
-#include <qhash.h>
-#include <qmetatype.h>
+#include <qmakeevaluator.h>
QT_BEGIN_NAMESPACE
-class QMakeProperty;
-
-struct ParsableBlock;
-struct IteratorBlock;
-struct FunctionBlock;
-
-class QMakeProject
+class QMakeProject : private QMakeEvaluator
{
- struct ScopeBlock
- {
- enum TestStatus { TestNone, TestFound, TestSeek };
- ScopeBlock() : iterate(0), ignore(false), else_status(TestNone) { }
- ScopeBlock(bool i) : iterate(0), ignore(i), else_status(TestNone) { }
- ~ScopeBlock();
- IteratorBlock *iterate;
- uint ignore : 1, else_status : 2;
- };
- friend struct ParsableBlock;
- friend struct IteratorBlock;
- friend struct FunctionBlock;
-
- QStack<ScopeBlock> scope_blocks;
- QStack<FunctionBlock *> function_blocks;
- IteratorBlock *iterator;
- FunctionBlock *function;
- QHash<QString, FunctionBlock*> testFunctions, replaceFunctions;
-
- bool host_build;
- bool need_restart;
- bool own_prop;
- bool backslashWarned;
- QString project_build_root;
- QString conffile;
- QString superfile;
- QString cachefile;
- QString real_spec, short_spec;
- QString pfile;
- QMakeProperty *prop;
- void reset();
- ProStringList extra_configs;
- ProValueMap extra_vars;
- QHash<QString, QStringList> vars, init_vars, base_vars;
- bool parse(const QString &text, QHash<QString, QStringList> &place, int line_count=1);
-
- enum IncludeStatus {
- IncludeSuccess,
- IncludeFeatureAlreadyLoaded,
- IncludeFailure,
- IncludeNoExist,
- IncludeParseFailure
- };
- enum IncludeFlags {
- IncludeFlagNone = 0x00,
- IncludeFlagFeature = 0x01,
- IncludeFlagNewParser = 0x02,
- IncludeFlagNewProject = 0x04
- };
- IncludeStatus doProjectInclude(QString file, uchar flags, QHash<QString, QStringList> &place);
-
- bool doProjectCheckReqs(const QStringList &deps, QHash<QString, QStringList> &place);
- bool doVariableReplace(QString &str, QHash<QString, QStringList> &place);
- QStringList doVariableReplaceExpand(const QString &str, QHash<QString, QStringList> &place, bool *ok=0);
- void init(QMakeProperty *);
- void cleanup();
- void loadDefaults();
- void setupProject();
- QStringList &values(const QString &v, QHash<QString, QStringList> &place);
- QStringList magicValues(const QString &v, const QHash<QString, QStringList> &place) const;
- QStringList qmakeFeaturePaths();
+ QString m_projectFile;
public:
- QMakeProject(QMakeProperty *p = 0) { init(p); }
- QMakeProject(QMakeProject *p, const QHash<QString, QStringList> *nvars=0);
- ~QMakeProject();
-
- void setExtraVars(const ProValueMap &_vars) { extra_vars = _vars; }
- void setExtraConfigs(const ProStringList &_cfgs) { extra_configs = _cfgs; }
-
- enum { ReadProFile=0x01, ReadSetup=0x02, ReadFeatures=0x04, ReadAll=0xFF };
- inline bool parse(const QString &text) { return parse(text, vars); }
- bool read(const QString &project, uchar cmd=ReadAll);
- bool read(uchar cmd=ReadAll);
+ QMakeProject();
+ QMakeProject(QMakeProject *p);
- QStringList userExpandFunctions() { return replaceFunctions.keys(); }
- QStringList userTestFunctions() { return testFunctions.keys(); }
+ bool read(const QString &project, LoadFlags what = LoadAll);
- QString projectFile();
- QString buildRoot() const { return project_build_root; }
- QString confFile() const { return conffile; }
- QString cacheFile() const { return cachefile; }
- QString specDir() const { return real_spec; }
- inline QMakeProperty *properties() { return prop; }
+ QString projectFile() const { return m_projectFile; }
+ QString buildRoot() const { return m_buildRoot; }
+ QString confFile() const { return m_conffile; }
+ QString cacheFile() const { return m_cachefile; }
+ QString specDir() const { return m_qmakespecFull; }
- bool doProjectTest(QString str, QHash<QString, QStringList> &place);
- bool doProjectTest(QString func, const QString &params,
- QHash<QString, QStringList> &place);
- bool doProjectTest(QString func, QStringList args,
- QHash<QString, QStringList> &place);
- bool doProjectTest(QString func, QList<QStringList> args,
- QHash<QString, QStringList> &place);
- QStringList doProjectExpand(QString func, const QString &params,
- QHash<QString, QStringList> &place);
- QStringList doProjectExpand(QString func, QStringList args,
- QHash<QString, QStringList> &place);
- QStringList doProjectExpand(QString func, QList<QStringList> args,
- QHash<QString, QStringList> &place);
-
- QStringList expand(const QString &v);
- QString expand(const QString &v, const QString &file, int line);
+ ProString expand(const QString &v, const QString &file, int line);
QStringList expand(const ProKey &func, const QList<ProStringList> &args);
- bool test(const QString &v);
+ bool test(const QString &v)
+ { m_current.clear(); return evaluateConditional(v, QStringLiteral("(generator)")); }
bool test(const ProKey &func, const QList<ProStringList> &args);
- bool isActiveConfig(const QString &x, bool regex=false,
- QHash<QString, QStringList> *place=NULL);
- bool isSet(const ProKey &v) const { return (*(const ProValueMap *)&vars).contains(v); }
+ bool isSet(const ProKey &v) const { return m_valuemapStack.first().contains(v); }
bool isEmpty(const ProKey &v) const;
- ProStringList values(const ProKey &v) const { return (*(const ProValueMap *)&vars)[v]; }
- ProStringList &values(const ProKey &v) { return (*(ProValueMap *)&vars)[v]; }
- ProString first(const ProKey &v) const;
+ ProStringList &values(const ProKey &v) { return valuesRef(v); }
int intValue(const ProKey &v, int defaultValue = 0) const;
- const ProValueMap &variables() const { return *(const ProValueMap *)&vars; }
- ProValueMap &variables() { return *(ProValueMap *)&vars; }
+ const ProValueMap &variables() const { return m_valuemapStack.first(); }
+ ProValueMap &variables() { return m_valuemapStack.first(); }
void dump() const;
- bool isHostBuild() const { return host_build; }
-
-protected:
- friend class MakefileGenerator;
- bool read(const QString &file, QHash<QString, QStringList> &place);
- bool read(QTextStream &file, QHash<QString, QStringList> &place);
-
+ using QMakeEvaluator::LoadFlags;
+ using QMakeEvaluator::setExtraVars;
+ using QMakeEvaluator::setExtraConfigs;
+ using QMakeEvaluator::loadSpec;
+ using QMakeEvaluator::evaluateFeatureFile;
+ using QMakeEvaluator::evaluateConfigFeatures;
+ using QMakeEvaluator::evaluateExpression;
+ using QMakeEvaluator::values;
+ using QMakeEvaluator::first;
+ using QMakeEvaluator::isActiveConfig;
+ using QMakeEvaluator::isHostBuild;
+ using QMakeEvaluator::dirSep;
};
-Q_DECLARE_METATYPE(QMakeProject*)
-
-inline QString QMakeProject::projectFile()
-{
- return pfile;
-}
-
-inline ProString QMakeProject::first(const ProKey &v) const
-{
- const ProStringList &vals = values(v);
- if(vals.isEmpty())
- return ProString("");
- return vals.first();
-}
inline int QMakeProject::intValue(const ProKey &v, int defaultValue) const
{
diff --git a/qmake/qmake.pri b/qmake/qmake.pri
index 985e4de3d7..1bf603e874 100644
--- a/qmake/qmake.pri
+++ b/qmake/qmake.pri
@@ -5,7 +5,10 @@ DEFINES += QT_NO_TEXTCODEC QT_NO_LIBRARY QT_NO_COMPRESS QT_NO_UNICODETABLES \
QT_NO_GEOM_VARIANT QT_NO_DATASTREAM
#qmake code
-SOURCES += project.cpp property.cpp main.cpp generators/makefile.cpp \
+SOURCES += project.cpp property.cpp main.cpp \
+ library/ioutils.cpp library/proitems.cpp library/qmakeglobals.cpp \
+ library/qmakeparser.cpp library/qmakeevaluator.cpp library/qmakebuiltins.cpp \
+ generators/makefile.cpp \
generators/unix/unixmake2.cpp generators/unix/unixmake.cpp meta.cpp \
option.cpp generators/win32/winmakefile.cpp generators/win32/mingw_make.cpp \
generators/makefiledeps.cpp generators/metamakefile.cpp generators/mac/pbuilder_pbx.cpp \
@@ -18,7 +21,8 @@ SOURCES += project.cpp property.cpp main.cpp generators/makefile.cpp \
generators/win32/cesdkhandler.cpp
HEADERS += project.h property.h \
- library/qmake_global.h library/proitems.h \
+ library/qmake_global.h library/ioutils.h library/proitems.h library/qmakeglobals.h \
+ library/qmakeparser.h library/qmakeevaluator.h library/qmakeevaluator_p.h \
generators/makefile.h \
generators/unix/unixmake.h meta.h option.h cachekeys.h \
generators/win32/winmakefile.h generators/win32/mingw_make.h generators/projectgenerator.h \
diff --git a/qmake/qmake.pro b/qmake/qmake.pro
index 4b03d66fc1..0d92ac21ae 100644
--- a/qmake/qmake.pro
+++ b/qmake/qmake.pro
@@ -6,7 +6,8 @@
option(host_build)
CONFIG += console bootstrap
CONFIG -= qt shared app_bundle uic
-DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED
+DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED \
+ PROEVALUATOR_FULL PROEVALUATOR_DEBUG
DESTDIR = ../bin/
OBJECTS_DIR = .