aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@digia.com>2012-11-29 14:04:54 +0100
committerIsmo Haataja <ismo.haataja@digia.com>2012-12-21 13:43:28 +0100
commit5fa2a9d4795daa5f694cb6bca95ffc5b3b87ef7e (patch)
tree4b5c704d93b5e6a8f21c42d09e289dfe5a92ecc5
parent854b9fb45333c1ad4885050308a53b264c4687b0 (diff)
port qmakewrapper to Qt5v1.2.0
Use the new qmake parser. We're keeping a copy of the parser around to make building of this project less dependent of Qt's repository structure. Change-Id: Ie15f2bc42fb6969d15ce7a1f127109a3a7d7ed64 Reviewed-by: Ismo Haataja <ismo.haataja@digia.com>
-rw-r--r--Qt4VS2003/ComWrappers/QMake/QMake.pri37
-rw-r--r--Qt4VS2003/ComWrappers/QMake/QMake.pro2
-rw-r--r--Qt4VS2003/ComWrappers/QMake/demo.cpp7
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evalhandler.cpp67
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evalhandler.h59
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/README6
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.cpp168
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.h83
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/proitems.cpp463
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/proitems.h397
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmake_global.h67
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakebuiltins.cpp1648
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.cpp2039
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.h307
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator_p.h107
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.cpp368
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.h173
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.cpp1225
-rw-r--r--Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.h223
-rw-r--r--Qt4VS2003/ComWrappers/QMake/qmakedataprovider.cpp130
-rw-r--r--Qt4VS2003/ComWrappers/QMake/qmakedataprovider.h4
-rw-r--r--Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.def8
-rw-r--r--Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.icobin0 -> 5974 bytes
-rw-r--r--Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.pro37
-rw-r--r--Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.rc2
25 files changed, 7498 insertions, 129 deletions
diff --git a/Qt4VS2003/ComWrappers/QMake/QMake.pri b/Qt4VS2003/ComWrappers/QMake/QMake.pri
index 17c10f0e..350c7703 100644
--- a/Qt4VS2003/ComWrappers/QMake/QMake.pri
+++ b/Qt4VS2003/ComWrappers/QMake/QMake.pri
@@ -1,24 +1,25 @@
-QMAKE_SOURCE_DIR=$$(QT_SOURCE_TREE)/qmake
+QMAKE_PARSER_DIR=$$PWD/evaluator
INCLUDEPATH += \
- $${QMAKE_SOURCE_DIR} \
- $$(QT_SOURCE_TREE)/tools/shared \
- $$PWD
-
-DEFINES += \
- QT_BUILD_QMAKE\
- QT_BUILD_QMAKE_LIBRARY QT_QMAKE_PARSER_ONLY
+ $$QMAKE_PARSER_DIR
HEADERS += \
- $$PWD/qmakedataprovider.h
+ $$PWD/evalhandler.h \
+ $$PWD/qmakedataprovider.h \
+ $$QMAKE_PARSER_DIR/ioutils.h \
+ $$QMAKE_PARSER_DIR/proitems.h \
+ $$QMAKE_PARSER_DIR/qmakeevaluator.h \
+ $$QMAKE_PARSER_DIR/qmakeevaluator_p.h \
+ $$QMAKE_PARSER_DIR/qmakeglobals.h \
+ $$QMAKE_PARSER_DIR/qmakeparser.h \
+ $$QMAKE_PARSER_DIR/qmake_global.h
SOURCES += \
- $${QMAKE_SOURCE_DIR}/option.cpp \
- $${QMAKE_SOURCE_DIR}/project.cpp \
- $${QMAKE_SOURCE_DIR}/property.cpp \
- $${QMAKE_SOURCE_DIR}/generators/metamakefile.cpp \
- $$(QT_SOURCE_TREE)/tools/shared/windows/registry.cpp \
- \
- $$PWD/qmakedataprovider.cpp
-
-LIBS += ole32.lib advapi32.lib
+ $$PWD/evalhandler.cpp \
+ $$PWD/qmakedataprovider.cpp \
+ $$QMAKE_PARSER_DIR/ioutils.cpp \
+ $$QMAKE_PARSER_DIR/proitems.cpp \
+ $$QMAKE_PARSER_DIR/qmakebuiltins.cpp \
+ $$QMAKE_PARSER_DIR/qmakeevaluator.cpp \
+ $$QMAKE_PARSER_DIR/qmakeglobals.cpp \
+ $$QMAKE_PARSER_DIR/qmakeparser.cpp
diff --git a/Qt4VS2003/ComWrappers/QMake/QMake.pro b/Qt4VS2003/ComWrappers/QMake/QMake.pro
index 219cf2e6..406bb0ef 100644
--- a/Qt4VS2003/ComWrappers/QMake/QMake.pro
+++ b/Qt4VS2003/ComWrappers/QMake/QMake.pro
@@ -7,5 +7,5 @@ CONFIG += \
TARGET = demo
SOURCES += \
- $${PWD}/demo.cpp \
+ demo.cpp
diff --git a/Qt4VS2003/ComWrappers/QMake/demo.cpp b/Qt4VS2003/ComWrappers/QMake/demo.cpp
index 9b9a9a9e..9297d20d 100644
--- a/Qt4VS2003/ComWrappers/QMake/demo.cpp
+++ b/Qt4VS2003/ComWrappers/QMake/demo.cpp
@@ -42,6 +42,7 @@
#include "qmakedataprovider.h"
#include <QDebug>
#include <QStringList>
+#include <QFileInfo>
void printList(const QString section, const QStringList values)
{
@@ -53,11 +54,13 @@ void printList(const QString section, const QStringList values)
int main(int argc, char *argv[])
{
- if (argc < 2)
+ if (argc < 2) {
+ fputs("Please provide a filename as argument.\n", stderr);
return -1;
+ }
QMakeDataProvider dataProvider;
- dataProvider.readFile(QString::fromLocal8Bit(argv[1]));
+ dataProvider.readFile(QFileInfo(QString::fromLocal8Bit(argv[1])).absoluteFilePath());
qDebug() << "valid ==" << dataProvider.isValid();
qDebug() << "flat ==" << dataProvider.isFlat();
diff --git a/Qt4VS2003/ComWrappers/QMake/evalhandler.cpp b/Qt4VS2003/ComWrappers/QMake/evalhandler.cpp
new file mode 100644
index 00000000..573b601f
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evalhandler.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt VS Add-in.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "evalhandler.h"
+
+void EvalHandler::message(int type, const QString &msg, const QString &fileName, int lineNo)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(msg);
+ Q_UNUSED(fileName);
+ Q_UNUSED(lineNo);
+}
+
+void EvalHandler::fileMessage(const QString &msg)
+{
+ Q_UNUSED(msg);
+}
+
+void EvalHandler::aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(proFile);
+ Q_UNUSED(type);
+}
+
+void EvalHandler::doneWithEval(ProFile *parent)
+{
+ Q_UNUSED(parent);
+}
diff --git a/Qt4VS2003/ComWrappers/QMake/evalhandler.h b/Qt4VS2003/ComWrappers/QMake/evalhandler.h
new file mode 100644
index 00000000..34719023
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evalhandler.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt VS Add-in.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVALHANDLER_H
+#define EVALHANDLER_H
+
+#include <qmakeevaluator.h>
+
+/**
+ * Dummy handler to please qmake's parser.
+ */
+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 *parent, ProFile *proFile, EvalFileType type);
+ void doneWithEval(ProFile *parent);
+};
+
+#endif // EVALHANDLER_H
diff --git a/Qt4VS2003/ComWrappers/QMake/evaluator/README b/Qt4VS2003/ComWrappers/QMake/evaluator/README
new file mode 100644
index 00000000..67994c5c
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/README
@@ -0,0 +1,6 @@
+This is a copy of qmake's parser and evaluator from qmake/library
+directory in the qtbase repository at git://gitorious.org/qt/qtbase.git.
+It has been taken at 5e525d283d308fe462e83955996fc53d80465c1b.
+
+If you do changes to these files, please make sure they are upstreamed!
+
diff --git a/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.cpp
new file mode 100644
index 00000000..94d69a68
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.h b/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.h
new file mode 100644
index 00000000..21fa32c3
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/ioutils.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.cpp
new file mode 100644
index 00000000..523a683f
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.cpp
@@ -0,0 +1,463 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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;
+}
+
+static QString ProStringList_join(const ProStringList &this_, const QChar *sep, const size_t sepSize)
+{
+ int totalLength = 0;
+ const int sz = this_.size();
+
+ for (int i = 0; i < sz; ++i)
+ totalLength += this_.at(i).size();
+
+ if (sz)
+ totalLength += sepSize * (sz - 1);
+
+ QString res(totalLength, Qt::Uninitialized);
+ QChar *ptr = (QChar *)res.constData();
+ for (int i = 0; i < sz; ++i) {
+ if (i) {
+ memcpy(ptr, sep, sepSize * sizeof(QChar));
+ ptr += sepSize;
+ }
+ const ProString &str = this_.at(i);
+ memcpy(ptr, str.constData(), str.size() * sizeof(QChar));
+ ptr += str.size();
+ }
+ return res;
+}
+
+QString ProStringList::join(const QString &sep) const
+{
+ return ProStringList_join(*this, sep.constData(), sep.size());
+}
+
+QString ProStringList::join(QChar sep) const
+{
+ return ProStringList_join(*this, &sep, 1);
+}
+
+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/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.h b/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.h
new file mode 100644
index 00000000..cb6a7dfc
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/proitems.h
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROITEMS_H
+#define PROITEMS_H
+
+#include "qmake_global.h"
+
+#include <qstring.h>
+#include <qvector.h>
+#include <qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+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
+#endif
+
+class ProKey;
+class ProStringList;
+class ProFile;
+
+class ProString {
+public:
+ 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) { 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; }
+ bool operator==(QLatin1String other) const { return toQStringRef() == other; }
+ bool operator==(const char *other) const { return toQStringRef() == QLatin1String(other); }
+ bool operator!=(const ProString &other) const { return !(*this == other); }
+ bool operator!=(const QString &other) const { return !(*this == other); }
+ bool operator!=(QLatin1String other) const { return !(*this == other); }
+ bool operator!=(const char *other) const { return !(*this == other); }
+ bool isNull() const { return m_string.isNull(); }
+ 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;
+ 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); }
+ bool startsWith(const ProString &sub) const { return toQStringRef().startsWith(sub.toQStringRef()); }
+ bool startsWith(const QString &sub) const { return toQStringRef().startsWith(sub); }
+ bool startsWith(const char *sub) const { return toQStringRef().startsWith(QLatin1String(sub)); }
+ bool startsWith(QChar c) const { return toQStringRef().startsWith(c); }
+ bool endsWith(const ProString &sub) const { return toQStringRef().endsWith(sub.toQStringRef()); }
+ bool endsWith(const QString &sub) const { return toQStringRef().endsWith(sub); }
+ bool endsWith(const char *sub) const { return toQStringRef().endsWith(QLatin1String(sub)); }
+ bool endsWith(QChar c) const { return toQStringRef().endsWith(c); }
+ int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(s, from, cs); }
+ int indexOf(const char *s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(QLatin1String(s), from, cs); }
+ int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(c, from, cs); }
+ int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(s, from, cs); }
+ int lastIndexOf(const char *s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(QLatin1String(s), from, cs); }
+ int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(c, from, cs); }
+ bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; }
+ bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; }
+ bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; }
+ int toInt(bool *ok = 0) const { return toQString().toInt(ok); } // XXX optimize
+ short toShort(bool *ok = 0) const { return toQString().toShort(ok); } // XXX optimize
+
+ 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;
+ QString &toQString(QString &tmp) const;
+
+ QByteArray toLatin1() const { return toQStringRef().toLatin1(); }
+
+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;
+ 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 class ProKey;
+};
+Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
+
+class ProKey : public ProString {
+public:
+ ALWAYS_INLINE ProKey() : ProString() {}
+ 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.
+ ALWAYS_INLINE ProKey(const ProKey &other) : ProString(other.toString()) {}
+ ALWAYS_INLINE ProKey &operator=(const ProKey &other)
+ {
+ toString() = other.toString();
+ return *this;
+ }
+#endif
+
+ ALWAYS_INLINE ProString &toString() { return *(ProString *)this; }
+ ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; }
+
+private:
+ ProKey(const ProString &other);
+};
+Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE);
+
+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)
+ { return ProString(one) + two; }
+
+inline QString operator+(const ProString &one, const char *two)
+ { return one + ProString(two); } // XXX optimize
+inline QString operator+(const char *one, const ProString &two)
+ { return ProString(one) + two; } // XXX optimize
+
+inline QString &operator+=(QString &that, const ProString &other)
+ { return that += other.toQStringRef(); }
+
+inline bool operator==(const QString &that, const ProString &other)
+ { return other == that; }
+inline bool operator!=(const QString &that, const ProString &other)
+ { return !(other == that); }
+
+QTextStream &operator<<(QTextStream &t, const ProString &str);
+
+class ProStringList : public QVector<ProString> {
+public:
+ ProStringList() {}
+ ProStringList(const ProString &str) { *this << str; }
+ explicit ProStringList(const QStringList &list);
+ QStringList toQStringList() const;
+
+ ProStringList &operator<<(const ProString &str)
+ { QVector<ProString>::operator<<(str); return *this; }
+
+ int length() const { return size(); }
+
+ QString join(const QString &sep) const;
+ QString join(QChar sep) const;
+
+ 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 contains(ProString(str), cs); }
+ bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+};
+Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE);
+
+inline ProStringList operator+(const ProStringList &one, const ProStringList &two)
+ { ProStringList ret = one; ret += two; return ret; }
+
+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
+ TokReturn, // previous literal/expansion is a return value
+ TokBreak, // break loop
+ TokNext, // shortcut to next loop iteration
+ 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/Qt4VS2003/ComWrappers/QMake/evaluator/qmake_global.h b/Qt4VS2003/ComWrappers/QMake/evaluator/qmake_global.h
new file mode 100644
index 00000000..3adf2e3b
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmake_global.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKE_GLOBAL_H
+#define QMAKE_GLOBAL_H
+
+#include <qglobal.h>
+
+#if defined(QMAKE_AS_LIBRARY)
+# if defined(QMAKE_LIBRARY)
+# define QMAKE_EXPORT Q_DECL_EXPORT
+# else
+# define QMAKE_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QMAKE_EXPORT
+#endif
+
+// Be fast even for debug builds
+// MinGW GCC 4.5+ has a problem with always_inline putTok and putBlockLen
+#if defined(__GNUC__) && !(defined(__MINGW32__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define ALWAYS_INLINE inline __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define ALWAYS_INLINE __forceinline
+#else
+# define ALWAYS_INLINE inline
+#endif
+
+#endif
diff --git a/Qt4VS2003/ComWrappers/QMake/evaluator/qmakebuiltins.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakebuiltins.cpp
new file mode 100644
index 00000000..bafa867f
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakebuiltins.cpp
@@ -0,0 +1,1648 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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_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 },
+ { "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;
+ }
+ m_parser->discardFileFromCache(qfi.filePath());
+ 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
+{
+ QByteArray out;
+#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
+ out = proc.readAllStandardOutput();
+# ifdef Q_OS_WIN
+ // FIXME: Qt's line end conversion on sequential files should really be fixed
+ out.replace("\r\n", "\n");
+# endif
+#else
+ 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);
+ }
+#endif
+ return out;
+}
+
+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(
+ int func_t, const ProKey &func, const ProStringList &args)
+{
+ ProStringList ret;
+
+ traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args));
+
+ 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) == ReturnTrue)
+ 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;
+ default:
+ evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1)));
+ break;
+ }
+
+ return ret;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
+ int func_t, const ProKey &function, const ProStringList &args)
+{
+ traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args));
+
+ 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_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();
+ VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly);
+ if (ok != ReturnTrue)
+ return ok;
+ if (args.count() == 2)
+ return returnBool(vars.contains(map(args.at(1))));
+ QRegExp regx;
+ 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_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();
+ VisitReturn ok;
+ if (parseInto.isEmpty()) {
+ ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags);
+ } else {
+ ProValueMap symbols;
+ if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags)) == ReturnTrue) {
+ 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;
+ }
+ }
+ if (ok == ReturnFalse && (flags & LoadSilent))
+ ok = ReturnTrue;
+ return ok;
+ }
+ 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;
+ }
+ VisitReturn ok = evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()),
+ ignore_error);
+ if (ok == ReturnFalse && ignore_error)
+ ok = ReturnTrue;
+ return ok;
+ }
+ 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
+ default:
+ evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1)));
+ return ReturnFalse;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.cpp
new file mode 100644
index 00000000..8f10066d
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.cpp
@@ -0,0 +1,2039 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeevaluator.h"
+#include "qmakeevaluator_p.h"
+
+#include "qmakeglobals.h"
+#include "qmakeparser.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_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_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;
+ case TokReturn:
+ m_returnValue = curr;
+ curr.clear();
+ ret = ReturnReturn;
+ goto ctrlstm;
+ case TokBreak:
+ ret = ReturnBreak;
+ goto ctrlstm;
+ case TokNext:
+ ret = ReturnNext;
+ ctrlstm:
+ if (!m_skipLevel && okey != or_op) {
+ traceMsg("flow control statement '%s', aborting block", dbgReturn(ret));
+ return ret;
+ }
+ traceMsg("skipped flow control statement '%s'", dbgReturn(ret));
+ okey = false, or_op = true; // force next evaluation
+ continue;
+ 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));
+
+ 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:
+
+ 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")) != ReturnTrue)
+ return false;
+ QString spec = m_qmakespec + QLatin1String("/qmake.conf");
+ if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
+ evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
+ return false;
+ }
+#ifndef QT_BUILD_QMAKE
+ // Legacy support for Qt4 default specs
+# ifdef Q_OS_UNIX
+ if (m_qmakespec.endsWith(QLatin1String("/default-host"))
+ || m_qmakespec.endsWith(QLatin1String("/default"))) {
+ QString rspec = QFileInfo(m_qmakespec).readLink();
+ if (!rspec.isEmpty())
+ m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(rspec));
+ }
+# 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"));
+ if (!orig_spec.isEmpty())
+ m_qmakespec = orig_spec.toQString();
+# endif
+#endif
+ valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespec);
+ m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
+ if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue)
+ 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) != ReturnTrue)
+ return false;
+ }
+ if (!m_conffile.isEmpty()) {
+ valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
+ if (evaluator.evaluateFile(
+ m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
+ return false;
+ }
+ if (!m_cachefile.isEmpty()) {
+ valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
+ if (evaluator.evaluateFile(
+ m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
+ 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 = propertyValue(ProKey(m_hostBuild ? "QMAKE_SPEC" : "QMAKE_XSPEC")).toQString();
+#ifndef QT_BUILD_QMAKE
+ // Legacy support for Qt4 qmake in Qt Creator, etc.
+ if (qmakespec.isEmpty())
+ qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
+#endif
+ 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) != ReturnTrue) {
+ 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) != ReturnTrue) {
+ return false;
+ }
+ if (!m_cachefile.isEmpty()
+ && evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
+ 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();
+ }
+ }
+}
+
+QMakeEvaluator::VisitReturn 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);
+ VisitReturn vr = evaluateFeatureFile(config, true);
+ if (vr == ReturnError)
+ return vr;
+ if (vr == ReturnTrue) {
+ finished = false;
+ break;
+ }
+ }
+ }
+ if (finished)
+ break;
+ }
+ return ReturnTrue;
+}
+
+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
+
+ VisitReturn vr;
+
+ m_handler->aboutToEval(currentProFile(), pro, type);
+ m_profileStack.push(pro);
+ valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+ if (flags & LoadPreFiles) {
+ setupProject();
+
+ if ((vr = evaluateFeatureFile(QLatin1String("default_pre.prf"))) == ReturnError)
+ goto failed;
+
+ 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()));
+ if ((vr = visitProBlock(pro, pro->tokPtr())) == ReturnError)
+ goto failed;
+ 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
+
+ if ((vr = evaluateFeatureFile(QLatin1String("default_post.prf"))) == ReturnError)
+ goto failed;
+
+ if ((vr = evaluateConfigFeatures()) == ReturnError)
+ goto failed;
+ }
+ vr = ReturnTrue;
+ failed:
+ m_profileStack.pop();
+ valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+ m_handler->doneWithEval(currentProFile());
+
+ return vr;
+}
+
+
+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_qmakespec.isEmpty()) {
+ // The spec is already platform-dependent, so no subdirs here.
+ feature_roots << (m_qmakespec + features_concat);
+
+ // Also check directly under the root directory of the mkspecs collection
+ QDir specdir(m_qmakespec);
+ 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, VisitReturn *ok)
+{
+ VisitReturn vr;
+ ProStringList ret;
+
+ if (m_valuemapStack.count() >= 100) {
+ evalError(fL1S("Ran into infinite recursion (depth > 100)."));
+ vr = ReturnFalse;
+ } else {
+ m_valuemapStack.push(ProValueMap());
+ m_locationStack.push(m_current);
+
+ ProStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i) {
+ args += argumentsList[i];
+ m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
+ }
+ m_valuemapStack.top()[statics.strARGS] = args;
+ vr = visitProBlock(func.pro(), func.tokPtr());
+ if (vr == ReturnReturn)
+ vr = ReturnTrue;
+ ret = m_returnValue;
+ m_returnValue.clear();
+
+ m_current = m_locationStack.pop();
+ m_valuemapStack.pop();
+ }
+ if (ok)
+ *ok = vr;
+ if (vr == ReturnTrue)
+ return ret;
+ return ProStringList();
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
+ const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
+ const ProString &function)
+{
+ VisitReturn vr;
+ ProStringList ret = evaluateFunction(func, argumentsList, &vr);
+ if (vr == ReturnTrue) {
+ if (ret.isEmpty())
+ return ReturnTrue;
+ if (ret.at(0) != statics.strfalse) {
+ if (ret.at(0) == statics.strtrue)
+ return ReturnTrue;
+ bool ok;
+ 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;
+ }
+ return vr;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
+ const ProKey &func, const ushort *&tokPtr)
+{
+ if (int func_t = statics.functions.value(func)) {
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinConditional(func_t, func, expandVariableReferences(tokPtr, 5, true));
+ }
+
+ 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);
+ }
+
+ skipExpression(tokPtr);
+ evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQString(m_tmp1)));
+ return ReturnFalse;
+}
+
+ProStringList QMakeEvaluator::evaluateExpandFunction(
+ const ProKey &func, const ushort *&tokPtr)
+{
+ if (int func_t = statics.expands.value(func)) {
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinExpand(func_t, func, expandVariableReferences(tokPtr, 5, true));
+ }
+
+ 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);
+ }
+
+ skipExpression(tokPtr);
+ evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQString(m_tmp1)));
+ return ProStringList();
+}
+
+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();
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
+ const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+ if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
+ m_locationStack.push(m_current);
+ VisitReturn ok = visitProFile(pro, type, flags);
+ m_current = m_locationStack.pop();
+ pro->deref();
+#ifdef PROEVALUATOR_FULL
+ if (ok == ReturnTrue) {
+ 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 ReturnFalse;
+ }
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileChecked(
+ const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+ if (fileName.isEmpty())
+ return ReturnFalse;
+ QMakeEvaluator *ref = this;
+ do {
+ foreach (const ProFile *pf, ref->m_profileStack)
+ if (pf->fileName() == fileName) {
+ evalError(fL1S("Circular inclusion of %1.").arg(fileName));
+ return ReturnFalse;
+ }
+ } while ((ref = ref->m_caller));
+ return evaluateFile(fileName, type, flags);
+}
+
+QMakeEvaluator::VisitReturn 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 ReturnFalse;
+
+ 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 ReturnTrue;
+ }
+ already.append(afn);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+ bool cumulative = m_cumulative;
+ m_cumulative = false;
+#endif
+
+ // The path is fully normalized already.
+ VisitReturn ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+ m_cumulative = cumulative;
+#endif
+ return ok;
+}
+
+QMakeEvaluator::VisitReturn 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;
+ VisitReturn ret = visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags);
+ if (ret != ReturnTrue)
+ return ret;
+ *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 ReturnTrue;
+}
+
+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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.h b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.h
new file mode 100644
index 00000000..2284a671
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator.h
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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); }
+
+ VisitReturn evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type,
+ LoadFlags flags);
+ VisitReturn evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type,
+ LoadFlags flags);
+ VisitReturn evaluateFeatureFile(const QString &fileName, bool silent = false);
+ VisitReturn evaluateFileInto(const QString &fileName,
+ ProValueMap *values, // output-only
+ LoadFlags flags);
+ VisitReturn 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, VisitReturn *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(int func_t, const ProKey &function, const ProStringList &args);
+ VisitReturn evaluateBuiltinConditional(int func_t, 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;
+#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_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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator_p.h b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator_p.h
new file mode 100644
index 00000000..e1cfc833
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeevaluator_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.cpp
new file mode 100644
index 00000000..b1d79b07
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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(QMakeInternal::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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.h b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.h
new file mode 100644
index 00000000..1e0be76e
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeglobals.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.cpp b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.cpp
new file mode 100644
index 00000000..9bf716a8
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.cpp
@@ -0,0 +1,1225 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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 strreturn;
+ QString strnext;
+ QString strbreak;
+ 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.strreturn = QLatin1String("return");
+ statics.strnext = QLatin1String("next");
+ statics.strbreak = QLatin1String("break");
+ 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;
+}
+
+void QMakeParser::discardFileFromCache(const QString &fileName)
+{
+ if (m_cache)
+ m_cache->discardFile(fileName);
+}
+
+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 lineMarked = true; // For in-expression markers
+ ushort needSep = TokNewStr; // Met unquoted whitespace
+ 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++;
+ 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;
+ 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"));
+ }
+ }
+ *ptr++ = c;
+ nextChr:
+ if (cur == end)
+ goto lineEnd;
+ c = *cur++;
+ }
+
+ lineEnd:
+ if (lineCont) {
+ if (quote) {
+ *ptr++ = ' ';
+ } 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)
+{
+ uchar nest = m_blockstack.top().nest;
+ m_blockstack.resize(m_blockstack.size() + 1);
+ m_blockstack.top().special = special;
+ m_blockstack.top().start = tokPtr;
+ m_blockstack.top().nest = nest;
+ 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;
+ uchar nest;
+ if (m_tmp == statics.strfor) {
+ if (m_invert || m_operator == OrOperator) {
+ // '|' could actually work reasonably, but qmake does nonsense here.
+ parseError(fL1S("Unexpected operator in front of for()."));
+ bogusTest(tokPtr);
+ return;
+ }
+ flushCond(tokPtr);
+ putLineMarker(tokPtr);
+ 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);
+ m_blockstack.top().nest |= NestLoop;
+ 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:
+ if (m_invert) {
+ parseError(fL1S("Unexpected operator in front of function definition."));
+ bogusTest(tokPtr);
+ return;
+ }
+ flushScopes(tokPtr);
+ putLineMarker(tokPtr);
+ 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);
+ m_blockstack.top().nest = NestFunction;
+ return;
+ }
+ }
+ parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
+ return;
+ } else if (m_tmp == statics.strreturn) {
+ if (argc > 1) {
+ parseError(fL1S("return() requires zero or one argument."));
+ bogusTest(tokPtr);
+ return;
+ }
+ defType = TokReturn;
+ nest = NestFunction;
+ goto ctrlstm2;
+ } else if (m_tmp == statics.strnext) {
+ defType = TokNext;
+ goto ctrlstm;
+ } else if (m_tmp == statics.strbreak) {
+ defType = TokBreak;
+ ctrlstm:
+ if (*uce != TokFuncTerminator) {
+ parseError(fL1S("%1() requires zero arguments.").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ nest = NestLoop;
+ ctrlstm2:
+ if (m_invert) {
+ parseError(fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ if (!(m_blockstack.top().nest & nest)) {
+ parseError(fL1S("Unexpected %1().").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ finalizeTest(tokPtr);
+ putBlock(tokPtr, uce, ptr - uce - 1); // Only for TokReturn
+ putTok(tokPtr, defType);
+ 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."));
+ bogusTest(tokPtr);
+ 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/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.h b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.h
new file mode 100644
index 00000000..0bab5eea
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/QMake/evaluator/qmakeparser.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $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);
+
+ void discardFileFromCache(const QString &fileName);
+
+private:
+ enum ScopeNesting {
+ NestNone = 0,
+ NestLoop = 1,
+ NestFunction = 2
+ };
+
+ struct BlockScope {
+ BlockScope() : start(0), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
+ 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
+ uchar nest; // Into what control structures we are nested
+ };
+
+ 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/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.cpp b/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.cpp
index 23b559df..5e6f2247 100644
--- a/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.cpp
+++ b/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.cpp
@@ -40,56 +40,73 @@
****************************************************************************/
#include "qmakedataprovider.h"
-#include <project.h>
-#include <property.h>
-#include <option.h>
-#include <QString>
-#include <QStringList>
-#include <QHash>
-#include <QDir>
+#include "evalhandler.h"
+#include <qmakeevaluator.h>
+#include <qmakeglobals.h>
+#include <QtCore/QFileInfo>
+#include <QtCore/QList>
+#include <QtCore/QPair>
class QMakeDataProviderPrivate
{
public:
- QHash<QString, QStringList> m_vars;
+ QStringList m_headerFiles;
+ QStringList m_sourceFiles;
+ QStringList m_resourceFiles;
+ QStringList m_formFiles;
+ typedef QPair<QStringList *, ProKey> Mapping;
+ QList<Mapping> m_variableMappings;
bool m_valid;
bool m_flat;
QString m_qtdir;
- QMakeDataProviderPrivate(const QString fileName)
+ QMakeDataProviderPrivate()
{
- readFile(fileName);
+ m_variableMappings
+ << qMakePair(&m_headerFiles, ProKey("HEADERS"))
+ << qMakePair(&m_sourceFiles, ProKey("SOURCES"))
+ << qMakePair(&m_resourceFiles, ProKey("RESOURCES"))
+ << qMakePair(&m_formFiles, ProKey("FORMS"));
}
bool readFile(const QString &fileName)
{
- m_vars.clear();
+ QFileInfo fi(fileName);
+ if (fi.isRelative())
+ qWarning("qmakewrapper: expecting an absolute filename.");
+
+ m_headerFiles.clear();
+ m_sourceFiles.clear();
+ m_resourceFiles.clear();
+ m_formFiles.clear();
m_valid = false;
m_flat = true;
- if (fileName.isEmpty())
+ QMakeGlobals globals;
+ ProFileCache proFileCache;
+ EvalHandler handler;
+ QMakeParser parser(&proFileCache, &handler);
+ QMakeEvaluator evaluator(&globals, &parser, &handler);
+ if (evaluator.evaluateFile(fileName, QMakeHandler::EvalProjectFile,
+ QMakeEvaluator::LoadProOnly) != QMakeEvaluator::ReturnTrue)
+ {
+ qWarning("qmakewrapper: failed to parse %s", qPrintable(fileName));
return false;
+ }
- // NOTE: needed to make QMake code work
- Option::mkfile::do_cache = true;
- Option::mkfile::cachefile = m_qtdir + "\\.qmake.cache";
+ m_valid = true;
+ m_flat = evaluator.isActiveConfig(QStringLiteral("flat"));
- QMakeProperty prop;
- QMakeProject project(&prop);
+ foreach (const Mapping &mapping, m_variableMappings)
+ *mapping.first = evaluator.values(mapping.second).toQStringList();
- m_valid = project.read(fileName, QMakeProject::ReadProFile);
- if (m_valid) {
- m_vars = project.variables();
- m_flat = project.isActiveConfig("flat");
- }
- return m_valid;
+ return true;
}
};
-QMakeDataProvider::QMakeDataProvider(const QString fileName)
- : d(new QMakeDataProviderPrivate(fileName))
+QMakeDataProvider::QMakeDataProvider()
+ : d(new QMakeDataProviderPrivate())
{
- // noop
}
QMakeDataProvider::~QMakeDataProvider()
@@ -109,22 +126,22 @@ void QMakeDataProvider::setQtDir(const QString &qtdir)
QStringList QMakeDataProvider::getFormFiles() const
{
- return d->m_vars.value("FORMS");
+ return d->m_formFiles;
}
QStringList QMakeDataProvider::getHeaderFiles() const
{
- return d->m_vars.value("HEADERS");
+ return d->m_headerFiles;
}
QStringList QMakeDataProvider::getResourceFiles() const
{
- return d->m_vars.value("RESOURCES");
+ return d->m_resourceFiles;
}
QStringList QMakeDataProvider::getSourceFiles() const
{
- return d->m_vars.value("SOURCES");
+ return d->m_sourceFiles;
}
bool QMakeDataProvider::isFlat() const
@@ -136,56 +153,3 @@ bool QMakeDataProvider::isValid() const
{
return d->m_valid;
}
-
-////////////////////////////////////////////////////////////////////////////////
-// BEGIN -- Modified copy from QMake's main.cpp
-Q_GLOBAL_STATIC(QString, globalPwd);
-
-QString qmake_getpwd()
-{
- QString & pwd = *(globalPwd());
- if(pwd.isNull())
- pwd = QDir::currentPath();
- return pwd;
-}
-
-bool qmake_setpwd(const QString &p)
-{
- if(QDir::setCurrent(p)) {
- QString & pwd = *(globalPwd());
- pwd = QDir::currentPath();
- return true;
- }
- return false;
-}
-// END -- from QMake's main.cpp
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-// BEGIN -- Copy from QMake's generators/projectgenerator.cpp
-QString project_builtin_regx() //calculate the builtin regular expression..
-{
- QString ret;
- QStringList builtin_exts;
- builtin_exts << Option::c_ext << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts" << ".xlf" << ".qrc";
- builtin_exts += Option::h_ext + Option::cpp_ext;
- for(int i = 0; i < builtin_exts.size(); ++i) {
- if(!ret.isEmpty())
- ret += "; ";
- ret += QString("*") + builtin_exts[i];
- }
- return ret;
-}
-// END -- from QMake's generators/projectgenerator.cpp
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-// BEGIN -- Modified copy from QLibraryInfo
-QString
-QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
-{
- return ""; // Modified to return empty string
-}
-
-// END -- from QLibraryInfo
-////////////////////////////////////////////////////////////////////////////////
diff --git a/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.h b/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.h
index dba7e7ca..dc6d7576 100644
--- a/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.h
+++ b/Qt4VS2003/ComWrappers/QMake/qmakedataprovider.h
@@ -43,8 +43,8 @@
#define QMAKEDATAPROVIDER_H
#include <QtCore/QString>
+#include <QtCore/QStringList>
-class QStringList;
class QMakeDataProviderPrivate;
class QMakeDataProvider {
@@ -52,7 +52,7 @@ class QMakeDataProvider {
QMakeDataProviderPrivate * const d;
public:
- QMakeDataProvider(const QString fileName = QString());
+ QMakeDataProvider();
~QMakeDataProvider();
bool readFile(const QString &fileName);
diff --git a/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.def b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.def
new file mode 100644
index 00000000..5d824910
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.def
@@ -0,0 +1,8 @@
+; mfc_test.def : Declares the module parameters.
+
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+ DumpIDL PRIVATE
diff --git a/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.ico b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.ico
new file mode 100644
index 00000000..26425ea5
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.ico
Binary files differ
diff --git a/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.pro b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.pro
index 9dcb50bf..99f66009 100644
--- a/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.pro
+++ b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.pro
@@ -1,33 +1,24 @@
-!win32 {
- error("This lib can only be built under Windows!")
-}
-
-isEmpty(QT_BUILD_TREE):QT_BUILD_TREE=$(QTDIR)
-isEmpty(QT_SOURCE_TREE):QT_SOURCE_TREE=$$fromfile($$QT_BUILD_TREE/.qmake.cache, QT_SOURCE_TREE)
-
TEMPLATE = lib
-#CONFIG += qt dll qaxserver release debug_and_release
-CONFIG += qt dll qaxserver release
-QT += widgets
-
+QT += axserver
+CONFIG += qaxserver_no_postlink release
TARGET = q5makewrapper
+DESTDIR = ./
VERSION = 1.0.0
+DEF_FILE = qmakewrapper.def
+RC_FILE = qmakewrapper.rc
-DEF_FILE = $$(QT_SOURCE_TREE)/../qtactiveqt/src/activeqt/control/qaxserver.def
-RC_FILE = $$(QT_SOURCE_TREE)/../qtactiveqt/src/activeqt/control/qaxserver.rc
-
-include($$(QT_SOURCE_TREE)/mkspecs/features/win32/qaxserver.prf)
-QMAKE_POST_LINK += $$escape_expand(\\n\\t)
+NEWLINE = $$escape_expand(\\n\\t)
-CONFIG(debug, release|debug) {
- DEFINES += DEBUG
- QMAKE_POST_LINK += $$quote(aximp debug\\q5makewrapper1.dll)
-}
+!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK += $$NEWLINE
+QMAKE_POST_LINK += $$quote(idc q5makewrapper1.dll /idl q5makewrapper.idl -version 1.0.0$$NEWLINE)
+QMAKE_POST_LINK += $$quote(midl q5makewrapper.idl /nologo /tlb q5makewrapper.tlb$$NEWLINE)
+QMAKE_POST_LINK += $$quote(idc q5makewrapper1.dll /tlb q5makewrapper.tlb$$NEWLINE)
+QMAKE_POST_LINK += $$quote(idc q5makewrapper1.dll /regserver$$NEWLINE)
+QMAKE_POST_LINK += $$quote(aximp q5makewrapper1.dll)
+QMAKE_CLEAN += q5makewrapper.idl q5makewrapper.tlb
-CONFIG(release, release|debug) {
- QMAKE_POST_LINK += $$quote(aximp release\\q5makewrapper1.dll)
-}
+INCLUDEPATH += ../qmake
HEADERS = qmakewrapper.h
diff --git a/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.rc b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.rc
new file mode 100644
index 00000000..1f7bc634
--- /dev/null
+++ b/Qt4VS2003/ComWrappers/qmakewrapper/qmakewrapper.rc
@@ -0,0 +1,2 @@
+1 TYPELIB "qmakewrapper.rc"
+1 ICON DISCARDABLE "qmakewrapper.ico"