summaryrefslogtreecommitdiffstats
path: root/qmake/generators
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /qmake/generators
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'qmake/generators')
-rw-r--r--qmake/generators/integrity/gbuild.cpp442
-rw-r--r--qmake/generators/integrity/gbuild.h74
-rw-r--r--qmake/generators/mac/pbuilder_pbx.cpp1861
-rw-r--r--qmake/generators/mac/pbuilder_pbx.h88
-rw-r--r--qmake/generators/makefile.cpp3297
-rw-r--r--qmake/generators/makefile.h309
-rw-r--r--qmake/generators/makefiledeps.cpp961
-rw-r--r--qmake/generators/makefiledeps.h132
-rw-r--r--qmake/generators/metamakefile.cpp572
-rw-r--r--qmake/generators/metamakefile.h82
-rw-r--r--qmake/generators/projectgenerator.cpp511
-rw-r--r--qmake/generators/projectgenerator.h71
-rw-r--r--qmake/generators/symbian/initprojectdeploy_symbian.cpp379
-rw-r--r--qmake/generators/symbian/initprojectdeploy_symbian.h82
-rw-r--r--qmake/generators/symbian/symbian_makefile.h105
-rw-r--r--qmake/generators/symbian/symbiancommon.cpp1117
-rw-r--r--qmake/generators/symbian/symbiancommon.h133
-rw-r--r--qmake/generators/symbian/symmake.cpp1136
-rw-r--r--qmake/generators/symbian/symmake.h149
-rw-r--r--qmake/generators/symbian/symmake_abld.cpp523
-rw-r--r--qmake/generators/symbian/symmake_abld.h68
-rw-r--r--qmake/generators/symbian/symmake_sbsv2.cpp760
-rw-r--r--qmake/generators/symbian/symmake_sbsv2.h84
-rw-r--r--qmake/generators/unix/unixmake.cpp925
-rw-r--r--qmake/generators/unix/unixmake.h84
-rw-r--r--qmake/generators/unix/unixmake2.cpp1352
-rw-r--r--qmake/generators/win32/borland_bmake.cpp175
-rw-r--r--qmake/generators/win32/borland_bmake.h68
-rw-r--r--qmake/generators/win32/mingw_make.cpp516
-rw-r--r--qmake/generators/win32/mingw_make.h88
-rw-r--r--qmake/generators/win32/msbuild_objectmodel.cpp1940
-rw-r--r--qmake/generators/win32/msbuild_objectmodel.h192
-rw-r--r--qmake/generators/win32/msvc_nmake.cpp361
-rw-r--r--qmake/generators/win32/msvc_nmake.h78
-rw-r--r--qmake/generators/win32/msvc_objectmodel.cpp2909
-rw-r--r--qmake/generators/win32/msvc_objectmodel.h1141
-rw-r--r--qmake/generators/win32/msvc_vcproj.cpp1569
-rw-r--r--qmake/generators/win32/msvc_vcproj.h151
-rw-r--r--qmake/generators/win32/msvc_vcxproj.cpp74
-rw-r--r--qmake/generators/win32/msvc_vcxproj.h70
-rw-r--r--qmake/generators/win32/winmakefile.cpp895
-rw-r--r--qmake/generators/win32/winmakefile.h96
-rw-r--r--qmake/generators/xmloutput.cpp378
-rw-r--r--qmake/generators/xmloutput.h244
44 files changed, 26242 insertions, 0 deletions
diff --git a/qmake/generators/integrity/gbuild.cpp b/qmake/generators/integrity/gbuild.cpp
new file mode 100644
index 0000000000..17bd8f0e5a
--- /dev/null
+++ b/qmake/generators/integrity/gbuild.cpp
@@ -0,0 +1,442 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gbuild.h"
+#include "option.h"
+#include "meta.h"
+#include <qdir.h>
+#include <qregexp.h>
+#include <qcryptographichash.h>
+#include <qdebug.h>
+#include <stdlib.h>
+#include <time.h>
+#ifdef Q_OS_UNIX
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+unsigned int dllbase = 0x01000000;
+#define DLLOFFSET 0x600000
+
+GBuildMakefileGenerator::GBuildMakefileGenerator() : MakefileGenerator()
+{
+ nativebins << "moc" << "rcc" << "uic" << "bootstrap";
+}
+
+bool
+GBuildMakefileGenerator::write()
+{
+ QStringList tmp;
+ QString filename(Option::output.fileName());
+ QString pathtoremove(qmake_getpwd());
+ QString relpath(pathtoremove);
+ QString strtarget(project->first("TARGET"));
+ bool isnativebin = nativebins.contains(strtarget);
+ relpath.replace(Option::output_dir, "");
+
+ /* correct output for non-prl, non-recursive case */
+ QString outname(qmake_getpwd());
+ outname += QDir::separator();
+ outname += fileInfo(Option::output.fileName()).baseName();
+ outname += projectSuffix();
+ Option::output.close();
+ Option::output.setFileName(outname);
+ MakefileGenerator::openOutput(Option::output, QString());
+
+ if (strtarget != fileInfo(project->projectFile()).baseName()) {
+ QString gpjname(strtarget);
+ QString outputName(qmake_getpwd());
+ outputName += QDir::separator();
+ outputName += fileInfo(project->projectFile()).baseName();
+ outputName += projectSuffix();
+ QFile f(outputName);
+ f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
+ QTextStream t(&f);
+ t << "#!gbuild\n";
+ t << "[Project]\n";
+ t << gpjname << projectSuffix() << "\n";
+ if ((project->first("TEMPLATE") == "lib")
+ && project->isActiveConfig("shared"))
+ t << gpjname << "_shared" << projectSuffix() << "\n";
+ t.flush();
+ gpjname += projectSuffix();
+ Option::output.close();
+ Option::output.setFileName(gpjname);
+ MakefileGenerator::openOutput(Option::output, QString());
+ }
+
+ if ((project->first("TEMPLATE") == "app")
+ && (!isnativebin)) {
+ QTextStream t(&Option::output);
+ QString intname(strtarget);
+ intname += ".int";
+ /* this is for bulding an INTEGRITY application.
+ * generate the .int integrate file and the .gpj INTEGRITY Application
+ * project file, then go on with regular files */
+ t << "#!gbuild" << "\n";
+ t << "[INTEGRITY Application]" << "\n";
+ t << "\t:binDirRelative=.\n";
+ t << "\t-o " << strtarget << "\n";
+ t << intname << "\n";
+ t << strtarget << "_app" << projectSuffix() << "\n";
+ t.flush();
+
+ /* generate integrate file */
+ QFile f(intname);
+ f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
+ QTextStream ti(&f);
+ ti << "# This is a file automatically generated by qmake" << "\n";
+ ti << "# Modifications will be lost next time you run qmake" << "\n";
+ ti << "Kernel" << "\n";
+ ti << "\tFilename\tDynamicDownload" << "\n";
+ ti << "EndKernel" << "\n" << "\n";
+ ti << "AddressSpace" << "\n";
+ ti << "\tName\t" << strtarget << "\n";
+ ti << "\tFilename\t" << strtarget << "_app" << "\n";
+ ti << "\tMemoryPoolSize\t0x100000" << "\n";
+ ti << "\tLanguage\tC++" << "\n";
+ /* FIXME : heap size is huge to be big enough for every example
+ * it should probably be tailored for each example, btu there is no
+ * good way to guess that */
+ ti << "\tHeapSize\t0x00D00000" << "\n";
+ ti << "\tTask\tInitial" << "\n";
+ ti << "\t\tStackSize\t0x30000" << "\n";
+ ti << "\tEndTask" << "\n";
+ ti << "EndAddressSpace" << "\n";
+ ti.flush();
+
+ /* change current project file to <projectname>_app.gpj and continue
+ * generation */
+ filename.insert(filename.lastIndexOf("."), "_app");
+ Option::output.close();
+ Option::output.setFileName(filename);
+ MakefileGenerator::openOutput(Option::output, QString());
+ } else if ((project->first("TEMPLATE") == "lib")
+ && project->isActiveConfig("shared")) {
+ QString gpjname(strtarget);
+ gpjname += "_shared";
+ gpjname += projectSuffix();
+ QFile f(gpjname);
+ f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
+ QTextStream t(&f);
+ t << "#!gbuild\n"
+ "[Program]\n"
+ "\t-A libINTEGRITY.so\n"
+ "\t-A libc.so\n"
+ "\t-A libscxx.so\n"
+ "\t-A libQtCore.so\n"
+ "\t-e __ghsbegin_text\n"
+ "\t-startfile=-\n"
+ "\t:syslibraries=-\n"
+ "\t-Onolink\n";
+ t << "\t-o lib" << strtarget << ".so\n";
+ t << "\t-l" << strtarget << "\n";
+ t << "\t-extractall=-l" << strtarget << "\n";
+ t << "\t:outputDir=work/" << filename.section(QDir::separator(), 0, -1).remove(".gpj") << "\n";
+ t << strtarget << "_shared.ld\n";
+ t << "$(__OS_DIR)/intlib/sharedobjbssinit.c\n";
+ t.flush();
+
+ QFile fl(strtarget + "_shared.ld");
+ fl.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
+ QTextStream tl(&fl);
+ tl << "CONSTANTS {\n"
+ " __INTEGRITY_MinPageAlign = 16K\n"
+ " __INTEGRITY_MaxPageAlign = 16K\n"
+ " __INTEGRITY_LibCBaseAddress = \n";
+ tl << dllbase << "\n";
+ tl << "}\n"
+ "-sec\n"
+ "{\n"
+ " .picbase __INTEGRITY_LibCBaseAddress :\n"
+ " .text :\n"
+ " .syscall :\n"
+ " .intercall :\n"
+ " .interfunc :\n"
+ " .secinfo :\n"
+ " .rodata align(16) :\n"
+ " .fixaddr :\n"
+ " .fixtype :\n"
+ " .rombeg :\n"
+ " .textchecksum :\n"
+ " // The above sections may be large. Leave a bigger gap for large pages.\n"
+ " .pidbase align(__INTEGRITY_MaxPageAlign) :\n"
+ " .sdabase :\n"
+ " .data :\n"
+ " .toc :\n"
+ " .opd :\n"
+ " .datachecksum :\n"
+ " .bss align(__INTEGRITY_MinPageAlign) :\n"
+ " .heap :\n"
+ "}\n";
+ tl.flush();
+ dllbase += DLLOFFSET;
+ }
+
+ warn_msg(WarnParser, Option::output.fileName().toAscii());
+ QTextStream t(&Option::output);
+ QString primaryTarget(project->values("QMAKE_CXX").at(0));
+
+ pathtoremove += QDir::separator();
+ filename.remove(qmake_getpwd());
+
+ //HEADER
+ t << "#!gbuild" << "\n";
+
+ /* find the architecture out of the compiler name */
+ if (filename.endsWith("projects.gpj")) {
+ primaryTarget.remove(0, 5);
+ t << "macro QT_BUILD_DIR=%expand_path(.)\n";
+ t << "macro __OS_DIR=" << project->values("INTEGRITY_DIR").first() << "\n";
+ t << "primaryTarget=" << primaryTarget << "_integrity.tgt" << "\n";
+ t << "customization=util/integrity/qt.bod\n";
+ }
+ /* project type */
+ if (project->first("TEMPLATE") == "app") {
+ t << "[Program]" << "\n";
+ if (isnativebin) {
+ t << "\t:binDir=bin\n";
+ t << "\t-o " << strtarget << "\n";
+ } else {
+ t << "\t:binDirRelative=.\n";
+ t << "\t-o " << strtarget << "_app\n";
+ }
+ } else if (project->first("TEMPLATE") == "lib") {
+ t << "[Library]" << "\n";
+ t << "\t:binDir=lib" << "\n";
+ t << "\t-o lib" << strtarget << ".a" << "\n";
+ } else if (project->first("TEMPLATE") == "subdirs")
+ t << "[Project]" << "\n";
+ else
+ t << project->first("TEMPLATE") << "\n";
+
+ /* compilations options */
+ t << "\t:sourceDir=." << "\n";
+
+ t << "\t:outputDir=work" << relpath << "\n";
+ if (filename.endsWith("projects.gpj")) {
+ t << "\t:sourceDir=work\n";
+ t << "\t-Iwork\n";
+ t << "\t-Llib\n";
+ t << "\t";
+ QStringList &l = project->values("QMAKE_CXXFLAGS");
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if ((*it).startsWith("-"))
+ t << "\n" << "\t" << (*it);
+ else
+ t << " " << (*it);
+ }
+ t << "\n";
+ }
+ t << "\n";
+
+ t << varGlue("DEFINES", "\t-D", "\n\t-D", "\n");
+
+ t << "\t-I.\n\t-I" << specdir() << "\n";
+ t << varGlue("INCLUDEPATH", "\t-I", "\n\t-I", "\n");
+ t << "\t--cxx_include_directory .\n\t--cxx_include_directory " << specdir() << "\n";
+ t << varGlue("INCLUDEPATH", "\t--cxx_include_directory ", "\n\t--cxx_include_directory ", "\n");
+
+ if (project->first("TEMPLATE") == "app") {
+ /* include linker flags if it's an application */
+ QString src[] = { "QMAKE_LFLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LIBDIR_FLAGS", "QMAKE_LIBS", "LIBS", QString() };
+ for (int i = 0; !src[i].isNull(); i++) {
+ /* skip target libraries for native tools */
+ if (isnativebin && (i == 0))
+ continue;
+ t << "\t";
+ QStringList &l = project->values(src[i]);
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if ((*it).startsWith("-"))
+ t << "\n" << "\t" << (*it);
+ else
+ t << " " << (*it);
+ }
+ t << "\n";
+ }
+ }
+
+ /* first subdirectories/subprojects */
+ {
+ QStringList &l = project->values("SUBDIRS");
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QString gpjname((*it));
+ /* avoid native tools */
+ if (nativebins.contains(gpjname.section("_", -1)))
+ continue;
+ if (!project->first((*it) + ".subdir").isEmpty())
+ gpjname = project->first((*it) + ".subdir");
+ else
+ gpjname.replace("_", QDir::separator());
+ gpjname += QDir::separator() + gpjname.section(QDir::separator(), -1);
+ gpjname += projectSuffix();
+ /* make relative */
+ if (!project->values("QT_SOURCE_TREE").isEmpty()) {
+ gpjname.replace(project->values("QT_SOURCE_TREE").first() + QDir::separator(), "");
+ }
+ t << gpjname << "\n";
+ }
+ }
+
+ {
+ QStringList &l = project->values("RESOURCES");
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QString tmpstr((*it).replace(pathtoremove, ""));
+ t << tmpstr << "\t[Qt Resource]\n";
+ tmpstr = tmpstr.section(".", -2, -1).section(QDir::separator(), -1);
+ tmpstr.remove(".qrc");
+ t << "\t-name " << tmpstr << "\n";
+ tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "qrc_");
+ tmpstr.append(".cpp");
+ t << "\t-o work/" << tmpstr << "\n";
+ }
+ }
+ {
+ QStringList &l = project->values("FORMS");
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QString tmpstr((*it).replace(pathtoremove, ""));
+ t << tmpstr << "\t[Qt Dialog]\n";
+ tmpstr = tmpstr.section(".", 0, 0).section(QDir::separator(), -1);
+ tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "ui_");
+ tmpstr.remove(".ui");
+ tmpstr.append(".h");
+ t << "\t-o work/" << tmpstr << "\n";
+ }
+ }
+
+ /* source files for this project */
+ QString src[] = { "HEADERS", "SOURCES", QString() };
+ for (int i = 0; !src[i].isNull(); i++) {
+ QStringList &l = project->values(src[i]);
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if ((*it).isEmpty())
+ continue;
+ /* native tools aren't preprocessed */
+ if (!isnativebin)
+ t << writeOne((*it), pathtoremove);
+ else
+ t << (*it).remove(pathtoremove) << "\n";
+ }
+ }
+ t << "\n";
+
+ {
+ QStringList &l = project->values("GENERATED_SOURCES");
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ t << "work/" << (*it).section(QDir::separator(), -1) << "\n";
+ }
+ }
+
+ return true;
+}
+
+QString GBuildMakefileGenerator::writeOne(QString filename, QString pathtoremove)
+{
+ QString s("");
+ s += filename.remove(pathtoremove);
+ if (filename.endsWith(Option::h_ext.first())) {
+ QString corename(filename.section(QDir::separator(), -1));
+ corename.remove(Option::h_ext.first());
+ corename.append(Option::cpp_ext.first());
+ corename.prepend(Option::h_moc_mod);
+ s += "\t[MOC/Qt Header]\n";
+ s += "\t-o ";
+ s += "work/";
+ s += corename;
+ s += "\n";
+ } else if (filename.section(QDir::separator(), -1).startsWith("qrc_")) {
+ QString tmpstr(filename.section("/", -1).section(".", 0, -1).remove("qrc_").remove(".cpp"));
+ s += "\n\t:depends=";
+ s += tmpstr;
+ s += ".qrc";
+ s += "\n";
+ } else if (filename.endsWith(Option::cpp_ext.first())) {
+ QString tmpstr(filename.section("/", -1));
+// QString moctool(project->values("QMAKE_MOC").first());
+ QString filepath(pathtoremove);
+ if (!project->values("QT_SOURCE_TREE").isEmpty()) {
+ filepath.remove(project->values("QT_SOURCE_TREE").first());
+ filepath.remove(0, 1);
+ }
+// if (!project->values("QT_BUILD_TREE").isEmpty()) {
+// moctool.remove(project->values("QT_BUILD_TREE").first());
+// moctool.remove(0, 1);
+// }
+ s += "\n\t:preexecShellSafe='${QT_BUILD_DIR}/bin/moc ";
+// s += moctool;
+// s += " ";
+ s += varGlue("DEFINES", "-D", " -D", " ");
+ s += varGlue("INCLUDEPATH", "-I", " -I", " ");
+ s += filepath;
+ s += filename;
+ s += " -o ";
+ tmpstr.replace(Option::cpp_ext.first(), Option::cpp_moc_ext);
+ s += "work/";
+ s += tmpstr;
+ s += "\n";
+ } else
+ s += "\n";
+ return s;
+}
+
+bool
+GBuildMakefileGenerator::openOutput(QFile &file, const QString &build) const
+{
+ debug_msg(1, "file is %s", file.fileName().toLatin1().constData());
+ QFileInfo fi(file);
+ if (fi.filePath().isEmpty())
+ file.setFileName(qmake_getpwd() + QDir::separator() + file.fileName());
+ if (!file.fileName().endsWith(projectSuffix())) {
+ QString outputName(file.fileName());
+ outputName += QDir::separator();
+ outputName += fileInfo(project->projectFile()).baseName();
+ outputName += projectSuffix();
+ warn_msg(WarnParser, outputName.toAscii());
+ file.setFileName(outputName);
+ }
+ debug_msg(1, "file is %s", file.fileName().toLatin1().constData());
+ bool ret = MakefileGenerator::openOutput(file, QString());
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/integrity/gbuild.h b/qmake/generators/integrity/gbuild.h
new file mode 100644
index 0000000000..6b5b1a0a91
--- /dev/null
+++ b/qmake/generators/integrity/gbuild.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GBUILD_H
+#define GBUILD_H
+
+#include "makefile.h"
+
+QT_BEGIN_NAMESPACE
+
+class GBuildMakefileGenerator : public MakefileGenerator
+{
+ virtual bool write();
+
+ QString projectSuffix() const { return QString(".gpj"); };
+ QString writeOne(QString filename, QString pathtoremove = "");
+
+public:
+ GBuildMakefileGenerator();
+ ~GBuildMakefileGenerator();
+
+ virtual bool supportsMetaBuild() { return false; }
+ virtual bool openOutput(QFile &, const QString &) const;
+protected:
+ bool doPrecompiledHeaders() const { return false; }
+ virtual bool doDepends() const { return true; }
+ QStringList nativebins;
+
+};
+
+inline GBuildMakefileGenerator::~GBuildMakefileGenerator()
+{ }
+
+QT_END_NAMESPACE
+
+#endif // GBUILD_H
diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp
new file mode 100644
index 0000000000..19667cdbe4
--- /dev/null
+++ b/qmake/generators/mac/pbuilder_pbx.cpp
@@ -0,0 +1,1861 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "pbuilder_pbx.h"
+#include "option.h"
+#include "meta.h"
+#include <qdir.h>
+#include <qregexp.h>
+#include <qcryptographichash.h>
+#include <qdebug.h>
+#include <stdlib.h>
+#include <time.h>
+#ifdef Q_OS_UNIX
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+#ifdef Q_OS_DARWIN
+#include <ApplicationServices/ApplicationServices.h>
+#include <private/qcore_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define GENERATE_AGGREGRATE_SUBDIR
+
+// Note: this is fairly hacky, but it does the job...
+
+static QString qtMD5(const QByteArray &src)
+{
+ QByteArray digest = QCryptographicHash::hash(src, QCryptographicHash::Md5);
+ return QString::fromLatin1(digest.toHex());
+}
+
+ProjectBuilderMakefileGenerator::ProjectBuilderMakefileGenerator() : UnixMakefileGenerator()
+{
+
+}
+
+bool
+ProjectBuilderMakefileGenerator::writeMakefile(QTextStream &t)
+{
+ writingUnixMakefileGenerator = false;
+ if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
+ /* for now just dump, I need to generated an empty xml or something.. */
+ fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n",
+ var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData());
+ return true;
+ }
+
+ project->values("MAKEFILE").clear();
+ project->values("MAKEFILE").append("Makefile");
+ if(project->first("TEMPLATE") == "app" || project->first("TEMPLATE") == "lib")
+ return writeMakeParts(t);
+ else if(project->first("TEMPLATE") == "subdirs")
+ return writeSubDirs(t);
+ return false;
+}
+
+struct ProjectBuilderSubDirs {
+ QMakeProject *project;
+ QString subdir;
+ bool autoDelete;
+ ProjectBuilderSubDirs(QMakeProject *p, QString s, bool a=true) : project(p), subdir(s), autoDelete(a) { }
+ ~ProjectBuilderSubDirs() {
+ if(autoDelete)
+ delete project;
+ }
+};
+
+bool
+ProjectBuilderMakefileGenerator::writeSubDirs(QTextStream &t)
+{
+ if(project->isActiveConfig("generate_pbxbuild_makefile")) {
+ QString mkwrap = fileFixify(pbx_dir + Option::dir_sep + ".." + Option::dir_sep + project->first("MAKEFILE"),
+ qmake_getpwd());
+ QFile mkwrapf(mkwrap);
+ if(mkwrapf.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ debug_msg(1, "pbuilder: Creating file: %s", mkwrap.toLatin1().constData());
+ QTextStream mkwrapt(&mkwrapf);
+ writingUnixMakefileGenerator = true;
+ UnixMakefileGenerator::writeSubDirs(mkwrapt);
+ writingUnixMakefileGenerator = false;
+ }
+ }
+
+ //HEADER
+ const int pbVersion = pbuilderVersion();
+ t << "// !$*UTF8*$!" << "\n"
+ << "{" << "\n"
+ << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";" << "\n"
+ << "\t" << "classes = {" << "\n" << "\t" << "};" << "\n"
+ << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";" << "\n"
+ << "\t" << "objects = {" << endl;
+
+ //SUBDIRS
+ QList<ProjectBuilderSubDirs*> pb_subdirs;
+ pb_subdirs.append(new ProjectBuilderSubDirs(project, QString(), false));
+ QString oldpwd = qmake_getpwd();
+ QMap<QString, QStringList> groups;
+ for(int pb_subdir = 0; pb_subdir < pb_subdirs.size(); ++pb_subdir) {
+ ProjectBuilderSubDirs *pb = pb_subdirs[pb_subdir];
+ const QStringList subdirs = pb->project->values("SUBDIRS");
+ for(int subdir = 0; subdir < subdirs.count(); subdir++) {
+ QString tmp = subdirs[subdir];
+ if(!pb->project->isEmpty(tmp + ".file"))
+ tmp = pb->project->first(tmp + ".file");
+ else if(!pb->project->isEmpty(tmp + ".subdir"))
+ tmp = pb->project->first(tmp + ".subdir");
+ if(fileInfo(tmp).isRelative() && !pb->subdir.isEmpty()) {
+ QString subdir = pb->subdir;
+ if(!subdir.endsWith(Option::dir_sep))
+ subdir += Option::dir_sep;
+ tmp = subdir + tmp;
+ }
+ QFileInfo fi(fileInfo(Option::fixPathToLocalOS(tmp, true)));
+ if(fi.exists()) {
+ if(fi.isDir()) {
+ QString profile = tmp;
+ if(!profile.endsWith(Option::dir_sep))
+ profile += Option::dir_sep;
+ profile += fi.baseName() + Option::pro_ext;
+ fi = QFileInfo(profile);
+ }
+ QMakeProject tmp_proj;
+ QString dir = fi.path(), fn = fi.fileName();
+ if(!dir.isEmpty()) {
+ if(!qmake_setpwd(dir))
+ fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData());
+ }
+ if(tmp_proj.read(fn)) {
+ if(Option::debug_level) {
+ debug_msg(1, "Dumping all variables:");
+ QMap<QString, QStringList> &vars = tmp_proj.variables();
+ for(QMap<QString, QStringList>::Iterator it = vars.begin();
+ it != vars.end(); ++it) {
+ if(it.key().left(1) != "." && !it.value().isEmpty())
+ debug_msg(1, "%s: %s === %s", fn.toLatin1().constData(), it.key().toLatin1().constData(),
+ it.value().join(" :: ").toLatin1().constData());
+ }
+ }
+ if(tmp_proj.first("TEMPLATE") == "subdirs") {
+ QMakeProject *pp = new QMakeProject(&tmp_proj);
+ pp->read(0);
+ pb_subdirs += new ProjectBuilderSubDirs(pp, dir);
+ } else if(tmp_proj.first("TEMPLATE") == "app" || tmp_proj.first("TEMPLATE") == "lib") {
+ QString pbxproj = qmake_getpwd() + Option::dir_sep + tmp_proj.first("TARGET") + projectSuffix();
+ if(!exists(pbxproj)) {
+ warn_msg(WarnLogic, "Ignored (not found) '%s'", pbxproj.toLatin1().constData());
+ goto nextfile; // # Dirty!
+ }
+ const QString project_key = keyFor(pbxproj + "_PROJECTREF");
+ project->values("QMAKE_PBX_SUBDIRS") += pbxproj;
+ //PROJECTREF
+ {
+ bool in_root = true;
+ QString name = qmake_getpwd();
+ if(project->isActiveConfig("flat")) {
+ QString flat_file = fileFixify(name, oldpwd, Option::output_dir, FileFixifyRelative);
+ if(flat_file.indexOf(Option::dir_sep) != -1) {
+ QStringList dirs = flat_file.split(Option::dir_sep);
+ name = dirs.back();
+ }
+ } else {
+ QString flat_file = fileFixify(name, oldpwd, Option::output_dir, FileFixifyRelative);
+ if(QDir::isRelativePath(flat_file) && flat_file.indexOf(Option::dir_sep) != -1) {
+ QString last_grp("QMAKE_SUBDIR_PBX_HEIR_GROUP");
+ QStringList dirs = flat_file.split(Option::dir_sep);
+ name = dirs.back();
+ for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
+ QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp));
+ if(dir_it == dirs.begin()) {
+ if(!groups.contains(new_grp))
+ project->values("QMAKE_SUBDIR_PBX_GROUPS").append(new_grp_key);
+ } else {
+ if(!groups[last_grp].contains(new_grp_key))
+ groups[last_grp] += new_grp_key;
+ }
+ last_grp = new_grp;
+ }
+ groups[last_grp] += project_key;
+ in_root = false;
+ }
+ }
+ if(in_root)
+ project->values("QMAKE_SUBDIR_PBX_GROUPS") += project_key;
+ t << "\t\t" << project_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("lastKnownFileType", "wrapper.pb-project") << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(tmp_proj.first("TARGET") + projectSuffix())) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", pbxproj) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ //WRAPPER
+ t << "\t\t" << keyFor(pbxproj + "_WRAPPER") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXReferenceProxy", SettingsNoQuote) << ";" << "\n";
+ if(tmp_proj.first("TEMPLATE") == "app") {
+ t << "\t\t\t" << writeSettings("fileType", "wrapper.application") << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".app") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("fileType", "compiled.mach-o.dylib") << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".dylib") << ";" << "\n";
+ }
+ t << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("remoteRef", keyFor(pbxproj + "_WRAPPERREF")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ t << "\t\t" << keyFor(pbxproj + "_WRAPPERREF") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("containerPortal", project_key) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("proxyType", "2") << ";" << "\n"
+// << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE!!!")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ //PRODUCTGROUP
+ t << "\t\t" << keyFor(pbxproj + "_PRODUCTGROUP") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", project->values(pbxproj + "_WRAPPER"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Products") << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+#ifdef GENERATE_AGGREGRATE_SUBDIR
+ //TARGET (for aggregate)
+ {
+ //container
+ const QString container_proxy = keyFor(pbxproj + "_CONTAINERPROXY");
+ t << "\t\t" << container_proxy << " = {" << "\n"
+ << "\t\t\t" << writeSettings("containerPortal", project_key) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("proxyType", "1") << ";" << "\n"
+ << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_TARGET")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ //targetref
+ t << "\t\t" << keyFor(pbxproj + "_TARGETREF") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", fixForOutput(tmp_proj.first("TARGET") +" (from " + tmp_proj.first("TARGET") + projectSuffix() + ")")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("targetProxy", container_proxy) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+#endif
+ }
+ }
+ nextfile:
+ qmake_setpwd(oldpwd);
+ }
+ }
+ }
+ qDeleteAll(pb_subdirs);
+ pb_subdirs.clear();
+
+ for(QMap<QString, QStringList>::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) {
+ t << "\t\t" << keyFor(grp_it.key()) << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(grp_it.key().section(Option::dir_sep, -1))) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER
+ //BUILDSTYLE
+ QString active_buildstyle;
+ for(int as_release = 0; as_release < 2; as_release++)
+ {
+ QMap<QString, QString> settings;
+ settings.insert("COPY_PHASE_STRIP", (as_release ? "YES" : "NO"));
+ if(as_release)
+ settings.insert("GCC_GENERATE_DEBUGGING_SYMBOLS", "NO");
+ if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK"))
+ settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK"));
+ {
+ const QStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS");
+ for(int i = 0; i < l.size(); ++i) {
+ QString name = l.at(i);
+ const QString value = project->values(name + QLatin1String(".value")).join(QString(Option::field_sep));
+ if(!project->isEmpty(name + QLatin1String(".name")))
+ name = project->values(name + QLatin1String(".name")).first();
+ settings.insert(name, value);
+ }
+ }
+
+ QString name;
+ if(pbVersion >= 42)
+ name = (as_release ? "Release" : "Debug");
+ else
+ name = (as_release ? "Deployment" : "Development");
+ if(pbVersion >= 42) {
+ QString key = keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_" + name);
+ project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n";
+ for(QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
+ t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";" << "\n";
+ t << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("name", name) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ QString key = keyFor("QMAKE_SUBDIR_PBX_BUILDSTYLE_" + name);
+ if(project->isActiveConfig("debug") != (bool)as_release) {
+ project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES").append(key);
+ active_buildstyle = name;
+ } else if(pbVersion >= 42) {
+ project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES").append(key);
+ }
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n";
+ for(QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
+ t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n";
+ t << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildStyle", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", name) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ if(pbVersion >= 42) {
+ t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildConfigurations", project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("defaultConfigurationIsName", active_buildstyle) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+#ifdef GENERATE_AGGREGRATE_SUBDIR
+ //target
+ t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_AGGREGATE_TARGET") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildPhases", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n"
+ << "\t\t\t\t" << writeSettings("PRODUCT_NAME", project->values("TARGET").first()) << ";" << "\n"
+ << "\t\t\t" << "};" << "\n";
+ {
+ QStringList dependencies;
+ const QStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS");
+ for(int i = 0; i < qmake_subdirs.count(); i++)
+ dependencies += keyFor(qmake_subdirs[i] + "_TARGETREF");
+ t << "\t\t\t" << writeSettings("dependencies", dependencies, SettingsAsList, 4) << ";" << "\n"
+ }
+ t << "\t\t\t" << writeSettings("isa", "PBXAggregateTarget", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", project->values("TARGET").first()) << ";" << "\n"
+ << "\t\t\t" << writeSettings("productName", project->values("TARGET").first()) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+#endif
+
+ //ROOT_GROUP
+ t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", project->values("QMAKE_SUBDIR_PBX_GROUPS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+
+
+ //ROOT
+ t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT") << " = {" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n"
+ << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("buildStyles", project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("projectDirPath", QStringList()) << ";" << "\n";
+ if(pbVersion >= 42)
+ t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST")) << ";" << "\n";
+ t << "\t\t\t" << "projectReferences = (" << "\n";
+ {
+ QStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS");
+ for(int i = 0; i < qmake_subdirs.count(); i++) {
+ QString subdir = qmake_subdirs[i];
+ t << "\t\t\t\t" << "{" << "\n"
+ << "\t\t\t\t\t" << writeSettings("ProductGroup", keyFor(subdir + "_PRODUCTGROUP")) << ";" << "\n"
+ << "\t\t\t\t\t" << writeSettings("ProjectRef", keyFor(subdir + "_PROJECTREF")) << ";" << "\n"
+ << "\t\t\t\t" << "}," << "\n";
+ }
+ }
+ t << "\t\t\t" << ");" << "\n"
+ << "\t\t\t" << writeSettings("targets",
+#ifdef GENERATE_AGGREGRATE_SUBDIR
+ project->values("QMAKE_SUBDIR_AGGREGATE_TARGET"),
+#else
+ QStringList(),
+#endif
+ SettingsAsList, 4) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+
+ //FOOTER
+ t << "\t" << "};" << "\n"
+ << "\t" << writeSettings("rootObject", keyFor("QMAKE_SUBDIR_PBX_ROOT")) << ";" << "\n"
+ << "}" << endl;
+
+ return true;
+}
+
+class ProjectBuilderSources
+{
+ bool buildable, object_output;
+ QString key, group, compiler;
+public:
+ ProjectBuilderSources(const QString &key, bool buildable=false, const QString &group=QString(), const QString &compiler=QString(), bool producesObject=false);
+ QStringList files(QMakeProject *project) const;
+ inline bool isBuildable() const { return buildable; }
+ inline QString keyName() const { return key; }
+ inline QString groupName() const { return group; }
+ inline QString compilerName() const { return compiler; }
+ inline bool isObjectOutput(const QString &file) const {
+ bool ret = object_output;
+ for(int i = 0; !ret && i < Option::c_ext.size(); ++i) {
+ if(file.endsWith(Option::c_ext.at(i))) {
+ ret = true;
+ break;
+ }
+ }
+ for(int i = 0; !ret && i < Option::cpp_ext.size(); ++i) {
+ if(file.endsWith(Option::cpp_ext.at(i))) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+ }
+};
+
+ProjectBuilderSources::ProjectBuilderSources(const QString &k, bool b,
+ const QString &g, const QString &c, bool o) : buildable(b), object_output(o), key(k), group(g), compiler(c)
+{
+ if(group.isNull()) {
+ if(k == "SOURCES")
+ group = "Sources";
+ else if(k == "HEADERS")
+ group = "Headers";
+ else if(k == "QMAKE_INTERNAL_INCLUDED_FILES")
+ group = "Sources [qmake]";
+ else if(k == "GENERATED_SOURCES" || k == "GENERATED_FILES")
+ group = "Temporary Sources";
+ else
+ fprintf(stderr, "No group available for %s!\n", k.toLatin1().constData());
+ }
+}
+
+QStringList
+ProjectBuilderSources::files(QMakeProject *project) const
+{
+ QStringList ret = project->values(key);
+ if(key == "QMAKE_INTERNAL_INCLUDED_FILES") {
+ QString pfile = project->projectFile();
+ if(pfile != "(stdin)")
+ ret.prepend(pfile);
+ for(int i = 0; i < ret.size(); ++i) {
+ QStringList newret;
+ if(!ret.at(i).endsWith(Option::prf_ext))
+ newret.append(ret.at(i));
+ ret = newret;
+ }
+ }
+ if(key == "SOURCES" && project->first("TEMPLATE") == "app" && !project->isEmpty("ICON"))
+ ret.append(project->first("ICON"));
+ return ret;
+}
+
+
+bool
+ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
+{
+ QStringList tmp;
+ bool did_preprocess = false;
+
+ //HEADER
+ const int pbVersion = pbuilderVersion();
+ t << "// !$*UTF8*$!" << "\n"
+ << "{" << "\n"
+ << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";" << "\n"
+ << "\t" << "classes = {" << "\n" << "\t" << "};" << "\n"
+ << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";" << "\n"
+ << "\t" << "objects = {" << endl;
+
+ //MAKE QMAKE equivelant
+ if(!project->isActiveConfig("no_autoqmake") && project->projectFile() != "(stdin)") {
+ QString mkfile = pbx_dir + Option::dir_sep + "qt_makeqmake.mak";
+ QFile mkf(mkfile);
+ if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ writingUnixMakefileGenerator = true;
+ debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
+ QTextStream mkt(&mkf);
+ writeHeader(mkt);
+ mkt << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ writeMakeQmake(mkt);
+ mkt.flush();
+ mkf.close();
+ writingUnixMakefileGenerator = false;
+ }
+ QString phase_key = keyFor("QMAKE_PBX_MAKEQMAKE_BUILDPHASE");
+ mkfile = fileFixify(mkfile, qmake_getpwd());
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
+ t << "\t\t" << phase_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("generatedFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Qt Qmake") << ";" << "\n"
+ << "\t\t\t" << writeSettings("neededFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n"
+ << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ //DUMP SOURCES
+ QMap<QString, QStringList> groups;
+ QList<ProjectBuilderSources> sources;
+ sources.append(ProjectBuilderSources("SOURCES", true));
+ sources.append(ProjectBuilderSources("GENERATED_SOURCES", true));
+ sources.append(ProjectBuilderSources("GENERATED_FILES"));
+ sources.append(ProjectBuilderSources("HEADERS"));
+ sources.append(ProjectBuilderSources("QMAKE_INTERNAL_INCLUDED_FILES"));
+ if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = project->first((*it) + ".output");
+ if(project->isEmpty((*it) + ".output"))
+ continue;
+ QString name = (*it);
+ if(!project->isEmpty((*it) + ".name"))
+ name = project->first((*it) + ".name");
+ const QStringList &inputs = project->values((*it) + ".input");
+ for(int input = 0; input < inputs.size(); ++input) {
+ if(project->isEmpty(inputs.at(input)))
+ continue;
+ bool duplicate = false;
+ for(int i = 0; i < sources.size(); ++i) {
+ if(sources.at(i).keyName() == inputs.at(input)) {
+ duplicate = true;
+ break;
+ }
+ }
+ if(!duplicate) {
+ bool isObj = project->values((*it) + ".CONFIG").indexOf("no_link") == -1;
+ const QStringList &outputs = project->values((*it) + ".variable_out");
+ for(int output = 0; output < outputs.size(); ++output) {
+ if(outputs.at(output) != "OBJECT") {
+ isObj = false;
+ break;
+ }
+ }
+ sources.append(ProjectBuilderSources(inputs.at(input), true,
+ QString("Sources [") + name + "]", (*it), isObj));
+ }
+ }
+ }
+ }
+ for(int source = 0; source < sources.size(); ++source) {
+ QStringList &src_list = project->values("QMAKE_PBX_" + sources.at(source).keyName());
+ QStringList &root_group_list = project->values("QMAKE_PBX_GROUPS");
+
+ QStringList files = fileFixify(sources.at(source).files(project));
+ for(int f = 0; f < files.count(); ++f) {
+ QString file = files[f];
+ if(file.length() >= 2 && (file[0] == '"' || file[0] == '\'') && file[(int) file.length()-1] == file[0])
+ file = file.mid(1, file.length()-2);
+ if(!sources.at(source).compilerName().isNull() &&
+ !verifyExtraCompiler(sources.at(source).compilerName(), file))
+ continue;
+ if(file.endsWith(Option::prl_ext))
+ continue;
+
+ bool in_root = true;
+ QString src_key = keyFor(file), name = file;
+ if(project->isActiveConfig("flat")) {
+ QString flat_file = fileFixify(file, qmake_getpwd(), Option::output_dir, FileFixifyRelative);
+ if(flat_file.indexOf(Option::dir_sep) != -1) {
+ QStringList dirs = flat_file.split(Option::dir_sep);
+ name = dirs.back();
+ }
+ } else {
+ QString flat_file = fileFixify(file, qmake_getpwd(), Option::output_dir, FileFixifyRelative);
+ if(QDir::isRelativePath(flat_file) && flat_file.indexOf(Option::dir_sep) != -1) {
+ QString last_grp("QMAKE_PBX_" + sources.at(source).groupName() + "_HEIR_GROUP");
+ QStringList dirs = flat_file.split(Option::dir_sep);
+ name = dirs.back();
+ dirs.pop_back(); //remove the file portion as it will be added via src_key
+ for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
+ QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp));
+ if(dir_it == dirs.begin()) {
+ if(!src_list.contains(new_grp_key))
+ src_list.append(new_grp_key);
+ } else {
+ if(!groups[last_grp].contains(new_grp_key))
+ groups[last_grp] += new_grp_key;
+ }
+ last_grp = new_grp;
+ }
+ groups[last_grp] += src_key;
+ in_root = false;
+ }
+ }
+ if(in_root)
+ src_list.append(src_key);
+ //source reference
+ t << "\t\t" << src_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(name)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", escapeFilePath(file)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(file)), SettingsNoQuote) << ";" << "\n";
+ if(pbVersion >= 38) {
+ QString filetype;
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
+ if(file.endsWith((*cppit))) {
+ filetype = "sourcecode.cpp.cpp";
+ break;
+ }
+ }
+ if(!filetype.isNull())
+ t << "\t\t\t" << writeSettings("lastKnownFileType", filetype) << ";" << "\n";
+ }
+ t << "\t\t" << "};" << "\n";
+ if(sources.at(source).isBuildable()) { //build reference
+ QString build_key = keyFor(file + ".BUILDABLE");
+ t << "\t\t" << build_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("fileRef", src_key) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "settings = {" << "\n"
+ << "\t\t\t\t" << writeSettings("ATTRIBUTES", QStringList(), SettingsAsList, 5) << ";" << "\n"
+ << "\t\t\t" << "};" << "\n"
+ << "\t\t" << "};" << "\n";
+ if(sources.at(source).isObjectOutput(file))
+ project->values("QMAKE_PBX_OBJ").append(build_key);
+ }
+ }
+ if(!src_list.isEmpty()) {
+ QString group_key = keyFor(sources.at(source).groupName());
+ if(root_group_list.indexOf(group_key) == -1)
+ root_group_list += group_key;
+
+ QStringList &group = groups[sources.at(source).groupName()];
+ for(int src = 0; src < src_list.size(); ++src) {
+ if(group.indexOf(src_list.at(src)) == -1)
+ group += src_list.at(src);
+ }
+ }
+ }
+ for(QMap<QString, QStringList>::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) {
+ t << "\t\t" << keyFor(grp_it.key()) << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(grp_it.key().section(Option::dir_sep, -1))) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ //PREPROCESS BUILDPHASE (just a makefile)
+ {
+ QString mkfile = pbx_dir + Option::dir_sep + "qt_preprocess.mak";
+ QFile mkf(mkfile);
+ if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ writingUnixMakefileGenerator = true;
+ did_preprocess = true;
+ debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
+ QTextStream mkt(&mkf);
+ writeHeader(mkt);
+ mkt << "MOC = " << Option::fixPathToTargetOS(var("QMAKE_MOC")) << endl;
+ mkt << "UIC = " << Option::fixPathToTargetOS(var("QMAKE_UIC")) << endl;
+ mkt << "LEX = " << var("QMAKE_LEX") << endl;
+ mkt << "LEXFLAGS = " << var("QMAKE_LEXFLAGS") << endl;
+ mkt << "YACC = " << var("QMAKE_YACC") << endl;
+ mkt << "YACCFLAGS = " << var("QMAKE_YACCFLAGS") << endl;
+ mkt << "DEFINES = "
+ << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << varGlue("DEFINES","-D"," -D","") << endl;
+ mkt << "INCPATH = " << "-I" << specdir();
+ if(!project->isActiveConfig("no_include_pwd")) {
+ QString pwd = escapeFilePath(fileFixify(qmake_getpwd()));
+ if(pwd.isEmpty())
+ pwd = ".";
+ mkt << " -I" << pwd;
+ }
+ {
+ const QStringList &incs = project->values("INCLUDEPATH");
+ for(QStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit)
+ mkt << " " << "-I" << escapeFilePath((*incit));
+ }
+ if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS"))
+ mkt << " " << var("QMAKE_FRAMEWORKPATH_FLAGS");
+ mkt << endl;
+ mkt << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ mkt << "MOVE = " << var("QMAKE_MOVE") << endl << endl;
+ mkt << "IMAGES = " << varList("QMAKE_IMAGE_COLLECTION") << endl;
+ mkt << "PARSERS =";
+ if(!project->isEmpty("YACCSOURCES")) {
+ QStringList &yaccs = project->values("YACCSOURCES");
+ for(QStringList::Iterator yit = yaccs.begin(); yit != yaccs.end(); ++yit) {
+ QFileInfo fi(fileInfo((*yit)));
+ mkt << " " << fi.path() << Option::dir_sep << fi.baseName()
+ << Option::yacc_mod << Option::cpp_ext.first();
+ }
+ }
+ if(!project->isEmpty("LEXSOURCES")) {
+ QStringList &lexs = project->values("LEXSOURCES");
+ for(QStringList::Iterator lit = lexs.begin(); lit != lexs.end(); ++lit) {
+ QFileInfo fi(fileInfo((*lit)));
+ mkt << " " << fi.path() << Option::dir_sep << fi.baseName()
+ << Option::lex_mod << Option::cpp_ext.first();
+ }
+ }
+ mkt << "\n";
+ mkt << "preprocess: $(PARSERS) compilers" << endl;
+ mkt << "clean preprocess_clean: parser_clean compiler_clean" << endl << endl;
+ mkt << "parser_clean:" << "\n";
+ if(!project->isEmpty("YACCSOURCES") || !project->isEmpty("LEXSOURCES"))
+ mkt << "\t-rm -f $(PARSERS)" << "\n";
+ writeExtraTargets(mkt);
+ if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
+ mkt << "compilers:";
+ const QStringList &compilers = project->values("QMAKE_EXTRA_COMPILERS");
+ for(int compiler = 0; compiler < compilers.size(); ++compiler) {
+ QString tmp_out = project->first(compilers.at(compiler) + ".output");
+ if(project->isEmpty(compilers.at(compiler) + ".output"))
+ continue;
+ const QStringList &inputs = project->values(compilers.at(compiler) + ".input");
+ for(int input = 0; input < inputs.size(); ++input) {
+ if(project->isEmpty(inputs.at(input)))
+ continue;
+ const QStringList &files = project->values(inputs.at(input));
+ for(int file = 0, added = 0; file < files.size(); ++file) {
+ if(!verifyExtraCompiler(compilers.at(compiler), files.at(file)))
+ continue;
+ if(added && !(added % 3))
+ mkt << "\\\n\t";
+ ++added;
+ const QString file_name = fileFixify(files.at(file), Option::output_dir, Option::output_dir);
+ mkt << " " << replaceExtraCompilerVariables(tmp_out, file_name, QString());
+ }
+ }
+ }
+ mkt << endl;
+ writeExtraCompilerTargets(mkt);
+ writingUnixMakefileGenerator = false;
+ }
+ mkt.flush();
+ mkf.close();
+ }
+ mkfile = fileFixify(mkfile, qmake_getpwd());
+ QString phase_key = keyFor("QMAKE_PBX_PREPROCESS_TARGET");
+// project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
+ t << "\t\t" << phase_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("generatedFileNames", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Qt Preprocessors") << ";" << "\n"
+ << "\t\t\t" << writeSettings("neededFileNames", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n"
+ << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ //SOURCE BUILDPHASE
+ if(!project->isEmpty("QMAKE_PBX_OBJ")) {
+ QString grp = "Build Sources", key = keyFor(grp);
+ project->values("QMAKE_PBX_BUILDPHASES").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXSourcesBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", grp) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ if(!project->isActiveConfig("staticlib")) { //DUMP LIBRARIES
+ QStringList &libdirs = project->values("QMAKE_PBX_LIBPATHS"),
+ &frameworkdirs = project->values("QMAKE_FRAMEWORKPATH");
+ QString libs[] = { "QMAKE_LFLAGS", "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS",
+ "QMAKE_LIBS", QString() };
+ for(int i = 0; !libs[i].isNull(); i++) {
+ tmp = project->values(libs[i]);
+ for(int x = 0; x < tmp.count();) {
+ bool remove = false;
+ QString library, name, opt = tmp[x].trimmed();
+ if(opt.length() >= 2 && (opt[0] == '"' || opt[0] == '\'') &&
+ opt[(int) opt.length()-1] == opt[0])
+ opt = opt.mid(1, opt.length()-2);
+ if(opt.startsWith("-L")) {
+ QString r = opt.right(opt.length() - 2);
+ fixForOutput(r);
+ libdirs.append(r);
+ } else if(opt == "-prebind") {
+ project->values("QMAKE_DO_PREBINDING").append("TRUE");
+ remove = true;
+ } else if(opt.startsWith("-l")) {
+ name = opt.right(opt.length() - 2);
+ QString lib("lib" + name);
+ for(QStringList::Iterator lit = libdirs.begin(); lit != libdirs.end(); ++lit) {
+ if(project->isActiveConfig("link_prl")) {
+ /* This isn't real nice, but it is real useful. This looks in a prl
+ for what the library will ultimately be called so we can stick it
+ in the ProjectFile. If the prl format ever changes (not likely) then
+ this will not really work. However, more concerning is that it will
+ encode the version number in the Project file which might be a bad
+ things in days to come? --Sam
+ */
+ QString lib_file = (*lit) + Option::dir_sep + lib;
+ if(QMakeMetaInfo::libExists(lib_file)) {
+ QMakeMetaInfo libinfo;
+ if(libinfo.readLib(lib_file)) {
+ if(!libinfo.isEmpty("QMAKE_PRL_TARGET")) {
+ library = (*lit) + Option::dir_sep + libinfo.first("QMAKE_PRL_TARGET");
+ debug_msg(1, "pbuilder: Found library (%s) via PRL %s (%s)",
+ opt.toLatin1().constData(), lib_file.toLatin1().constData(), library.toLatin1().constData());
+ remove = true;
+ }
+ }
+ }
+ }
+ if(!remove) {
+ QString extns[] = { ".dylib", ".so", ".a", QString() };
+ for(int n = 0; !remove && !extns[n].isNull(); n++) {
+ QString tmp = (*lit) + Option::dir_sep + lib + extns[n];
+ if(exists(tmp)) {
+ library = tmp;
+ debug_msg(1, "pbuilder: Found library (%s) via %s",
+ opt.toLatin1().constData(), library.toLatin1().constData());
+ remove = true;
+ }
+ }
+ }
+ }
+ } else if(opt.startsWith("-F")) {
+ QString r;
+ if(opt.size() > 2) {
+ r = opt.right(opt.length() - 2);
+ } else {
+ if(x == tmp.count()-1)
+ break;
+ r = tmp[++x];
+ }
+ if(!r.isEmpty()) {
+ fixForOutput(r);
+ frameworkdirs.append(r);
+ }
+ } else if(opt == "-framework") {
+ if(x == tmp.count()-1)
+ break;
+ const QString framework = tmp[x+1];
+ QStringList fdirs = frameworkdirs;
+ fdirs << "/System/Library/Frameworks/" << "/Library/Frameworks/";
+ for(int fdir = 0; fdir < fdirs.count(); fdir++) {
+ if(exists(fdirs[fdir] + QDir::separator() + framework + ".framework")) {
+ tmp.removeAt(x);
+ remove = true;
+ library = fdirs[fdir] + Option::dir_sep + framework + ".framework";
+ break;
+ }
+ }
+ } else if(opt.left(1) != "-") {
+ if(exists(opt)) {
+ remove = true;
+ library = opt;
+ }
+ }
+ if(!library.isEmpty()) {
+ const int slsh = library.lastIndexOf(Option::dir_sep);
+ if(name.isEmpty()) {
+ if(slsh != -1)
+ name = library.right(library.length() - slsh - 1);
+ }
+ if(slsh != -1) {
+ const QString path = QFileInfo(library.left(slsh)).absoluteFilePath();
+ if(!path.isEmpty() && !libdirs.contains(path))
+ libdirs += path;
+ }
+ library = fileFixify(library);
+ QString key = keyFor(library);
+ bool is_frmwrk = (library.endsWith(".framework"));
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", (is_frmwrk ? "PBXFrameworkReference" : "PBXFileReference"), SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(name)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", escapeFilePath(library)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(library)), SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ project->values("QMAKE_PBX_LIBRARIES").append(key);
+ QString build_key = keyFor(library + ".BUILDABLE");
+ t << "\t\t" << build_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("fileRef", key) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "settings = {" << "\n"
+ << "\t\t\t" << "};" << "\n"
+ << "\t\t" << "};" << "\n";
+ project->values("QMAKE_PBX_BUILD_LIBRARIES").append(build_key);
+ }
+ if(remove)
+ tmp.removeAt(x);
+ else
+ x++;
+ }
+ project->values(libs[i]) = tmp;
+ }
+ }
+ //SUBLIBS BUILDPHASE (just another makefile)
+ if(!project->isEmpty("SUBLIBS")) {
+ QString mkfile = pbx_dir + Option::dir_sep + "qt_sublibs.mak";
+ QFile mkf(mkfile);
+ if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ writingUnixMakefileGenerator = true;
+ debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
+ QTextStream mkt(&mkf);
+ writeHeader(mkt);
+ mkt << "SUBLIBS= ";
+ tmp = project->values("SUBLIBS");
+ for(int i = 0; i < tmp.count(); i++)
+ t << "tmp/lib" << tmp[i] << ".a ";
+ t << endl << endl;
+ mkt << "sublibs: $(SUBLIBS)" << endl << endl;
+ tmp = project->values("SUBLIBS");
+ for(int i = 0; i < tmp.count(); i++)
+ t << "tmp/lib" << tmp[i] << ".a" << ":\n\t"
+ << var(QString("MAKELIB") + tmp[i]) << endl << endl;
+ mkt.flush();
+ mkf.close();
+ writingUnixMakefileGenerator = false;
+ }
+ QString phase_key = keyFor("QMAKE_PBX_SUBLIBS_BUILDPHASE");
+ mkfile = fileFixify(mkfile, qmake_getpwd());
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
+ t << "\t\t" << phase_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("generatedFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Qt Sublibs") << ";" << "\n"
+ << "\t\t\t" << writeSettings("neededFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << "\n"
+ << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ //LIBRARY BUILDPHASE
+ if(!project->isEmpty("QMAKE_PBX_LIBRARIES")) {
+ tmp = project->values("QMAKE_PBX_LIBRARIES");
+ if(!tmp.isEmpty()) {
+ QString grp("External Frameworks and Libraries"), key = keyFor(grp);
+ project->values("QMAKE_PBX_GROUPS").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_LIBRARIES"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ }
+ {
+ QString grp("Frameworks & Libraries"), key = keyFor(grp);
+ project->values("QMAKE_PBX_BUILDPHASES").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", project->values("QMAKE_PBX_BUILD_LIBRARIES"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFrameworksBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ if(project->isActiveConfig("app_bundle") && project->first("TEMPLATE") == "app") { //BUNDLE RESOURCES
+ QString grp("Bundle Resources"), key = keyFor(grp);
+ project->values("QMAKE_PBX_BUILDPHASES").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "files = (" << "\n";
+ if(!project->isEmpty("ICON")) {
+ QString icon = project->first("ICON");
+ if(icon.length() >= 2 && (icon[0] == '"' || icon[0] == '\'') && icon[(int)icon.length()-1] == icon[0])
+ icon = icon.mid(1, icon.length()-2);
+ t << "\t\t\t\t" << keyFor(icon + ".BUILDABLE") << ",\n";
+ }
+ t << "\t\t\t" << ");" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXResourcesBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ { //INSTALL BUILDPHASE (copy)
+ QString phase_key = keyFor("QMAKE_PBX_TARGET_COPY_PHASE");
+ QString destDir = Option::output_dir;
+ if (!project->isEmpty("QMAKE_ORIG_DESTDIR"))
+ destDir = project->first("QMAKE_ORIG_DESTDIR");
+ destDir = fixForOutput(destDir);
+ destDir = fileInfo(Option::fixPathToLocalOS(destDir)).absoluteFilePath();
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
+ t << "\t\t" << phase_key << " = {\n"
+ << "\t\t\t" << writeSettings("name", "Project Copy") << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("dstPath", escapeFilePath(destDir)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("dstSubfolderSpec", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", keyFor("QMAKE_PBX_TARGET_COPY_FILE"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};\n"
+ << "\t\t" << keyFor("QMAKE_PBX_TARGET_COPY_FILE") << " = {\n"
+ << "\t\t\t" << writeSettings("fileRef", keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "settings = {\n"
+ << "\t\t\t" << "};\n"
+ << "\t\t" << "};\n";
+ }
+ //BUNDLE_DATA BUILDPHASE (copy)
+ if(!project->isEmpty("QMAKE_BUNDLE_DATA")) {
+ QStringList bundle_file_refs;
+ //all bundle data
+ const QStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA");
+ for(int i = 0; i < bundle_data.count(); i++) {
+ QStringList pbx_files;
+ //all files
+ const QStringList &files = project->values(bundle_data[i] + ".files");
+ for(int file = 0; file < files.count(); file++) {
+ QString file_ref_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE_REF." + bundle_data[i] + "-" + files[file]);
+ bundle_file_refs += file_ref_key;
+ t << "\t\t" << file_ref_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", escapeFilePath(files[file])) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(files[file])), SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ QString copy_file_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE." + bundle_data[i] + "-" + files[file]);
+ pbx_files += copy_file_key;
+ t << "\t\t" << copy_file_key << " = {\n"
+ << "\t\t\t" << writeSettings("fileRef", file_ref_key) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "settings = {\n"
+ << "\t\t\t" << "}" << ";" << "\n"
+ << "\t\t" << "}" << ";" << "\n";
+ }
+ //the phase
+ QString phase_key = keyFor("QMAKE_PBX_BUNDLE_COPY." + bundle_data[i]);
+ QString path;
+ if(!project->isEmpty(bundle_data[i] + ".version")) {
+ //###
+ }
+ path += project->first(bundle_data[i] + ".path");
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
+ t << "\t\t" << phase_key << " = {\n"
+ << "\t\t\t" << writeSettings("name", "Bundle Copy [" + bundle_data[i] + "]") << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("dstPath", escapeFilePath(path)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("dstSubfolderSpec", "1", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("files", pbx_files, SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "}" << ";" << "\n";
+ }
+ QString bundle_copy_key = keyFor("QMAKE_PBX_BUNDLE_COPY");
+ project->values("QMAKE_PBX_GROUPS").append(bundle_copy_key);
+ t << "\t\t" << bundle_copy_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", bundle_file_refs, SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Source [bundle data]") << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ if(/*pbVersion >= 38 &&*/ !project->isEmpty("QMAKE_PBX_PRESCRIPT_BUILDPHASES") && 0) {
+ // build reference
+ t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPT_BUILDREFERENCE") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("includeInIndex", "0") << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", "preprocessor.out") << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ project->values("QMAKE_PBX_PRODUCTS").append(keyFor("QMAKE_PBX_PRESCRIPTS_BUILDREFERENCE"));
+ //build phase
+ t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPTS_BUILDPHASE") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildSettings", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("dependencies", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Qt Preprocessor Steps") << ";" << "\n"
+ << "\t\t\t" << writeSettings("productName", "Qt Preprocessor Steps") << ";" << "\n"
+ << "\t\t\t" << writeSettings("productReference", keyFor("QMAKE_PBX_PRESCRIPTS_BUILDREFERENCE")) << ";" << "\n";
+ if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE"))
+ t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n";
+ else
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.tool") << ";" << "\n";
+ t << "\t\t" << "};" << "\n";
+ //dependency
+ t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPTS_DEPENDENCY") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("target", keyFor("QMAKE_PBX_PRESCRIPTS_BUILDPHASE")) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ project->values("QMAKE_PBX_TARGET_DEPENDS").append(keyFor("QMAKE_PBX_PRESCRIPTS_DEPENDENCY"));
+ project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").clear(); //these are already consumed above
+ }
+
+ //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER
+ //ROOT_GROUP
+ t << "\t\t" << keyFor("QMAKE_PBX_ROOT_GROUP") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_GROUPS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n"
+ << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ //REFERENCE
+ project->values("QMAKE_PBX_PRODUCTS").append(keyFor(pbx_dir + "QMAKE_PBX_REFERENCE"));
+ t << "\t\t" << keyFor(pbx_dir + "QMAKE_PBX_REFERENCE") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n";
+ if(project->first("TEMPLATE") == "app") {
+ QString targ = project->first("QMAKE_ORIG_TARGET");
+ if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
+ if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
+ targ = project->first("QMAKE_BUNDLE_NAME");
+ targ += project->first("QMAKE_BUNDLE_EXTENSION");
+ if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE"))
+ t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) + ";" << "\n";
+ } else if(project->isActiveConfig("app_bundle")) {
+ if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
+ targ = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
+ targ += ".app";
+ t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.application") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.executable") << ";" << "\n";
+ }
+ QString app = (!project->isEmpty("DESTDIR") ? project->first("DESTDIR") + project->first("QMAKE_ORIG_TARGET") :
+ qmake_getpwd()) + Option::dir_sep + targ;
+ t << "\t\t\t" << writeSettings("path", escapeFilePath(targ)) << ";" << "\n";
+ } else {
+ QString lib = project->first("QMAKE_ORIG_TARGET");
+ if(project->isActiveConfig("staticlib")) {
+ lib = project->first("TARGET");
+ } else if(!project->isActiveConfig("lib_bundle")) {
+ if(project->isActiveConfig("plugin"))
+ lib = project->first("TARGET");
+ else
+ lib = project->first("TARGET_");
+ }
+ int slsh = lib.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ lib = lib.right(lib.length() - slsh - 1);
+ if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
+ if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
+ lib = project->first("QMAKE_BUNDLE_NAME");
+ lib += project->first("QMAKE_BUNDLE_EXTENSION");
+ if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE"))
+ t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) << ";" << "\n";
+ } else if(!project->isActiveConfig("staticlib") && project->isActiveConfig("lib_bundle")) {
+ if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
+ lib = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
+ lib += ".framework";
+ t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.framework") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("explicitFileType", "compiled.mach-o.dylib") << ";" << "\n";
+ }
+ t << "\t\t\t" << writeSettings("path", escapeFilePath(lib)) << ";" << "\n";
+ }
+ t << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ { //Products group
+ QString grp("Products"), key = keyFor(grp);
+ project->values("QMAKE_PBX_GROUPS").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_PRODUCTS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", "Products") << ";" << "\n"
+ << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ //TARGET
+ QString target_key = keyFor(pbx_dir + "QMAKE_PBX_TARGET");
+ project->values("QMAKE_PBX_TARGETS").append(target_key);
+ t << "\t\t" << target_key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES") + project->values("QMAKE_PBX_BUILDPHASES"),
+ SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n";
+ QString cCompiler = project->first("QMAKE_CC");
+ if (!cCompiler.isEmpty()) {
+ t << "\t\t\t\t" << writeSettings("CC", fixForOutput(findProgram(cCompiler))) << ";" << "\n";
+ }
+ cCompiler = project->first("QMAKE_CXX");
+ if (!cCompiler.isEmpty()) {
+ t << "\t\t\t\t" << writeSettings("CPLUSPLUS", fixForOutput(findProgram(cCompiler))) << ";" << "\n";
+ }
+
+ t << "\t\t\t\t" << writeSettings("HEADER_SEARCH_PATHS", fixListForOutput("INCLUDEPATH") + QStringList(fixForOutput(specdir())), SettingsAsList, 5) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("LIBRARY_SEARCH_PATHS", fixListForOutput("QMAKE_PBX_LIBPATHS"), SettingsAsList, 5) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("OPTIMIZATION_CFLAGS", QStringList(), SettingsAsList, 5) << ";" << "\n";
+ {
+ QStringList cflags = fixListForOutput("QMAKE_CFLAGS");
+ const QStringList &prl_defines = project->values("PRL_EXPORT_DEFINES");
+ for(int i = 0; i < prl_defines.size(); ++i)
+ cflags += "-D" + prl_defines.at(i);
+ const QStringList &defines = project->values("DEFINES");
+ for(int i = 0; i < defines.size(); ++i)
+ cflags += "-D" + defines.at(i);
+ t << "\t\t\t\t" << writeSettings("OTHER_CFLAGS", cflags, SettingsAsList, 5) << ";" << "\n";
+ }
+ {
+ QStringList cxxflags = fixListForOutput("QMAKE_CXXFLAGS");
+ const QStringList &prl_defines = project->values("PRL_EXPORT_DEFINES");
+ for(int i = 0; i < prl_defines.size(); ++i)
+ cxxflags += "-D" + prl_defines.at(i);
+ const QStringList &defines = project->values("DEFINES");
+ for(int i = 0; i < defines.size(); ++i)
+ cxxflags += "-D" + defines.at(i);
+ t << "\t\t\t\t" << writeSettings("OTHER_CPLUSPLUSFLAGS", cxxflags, SettingsAsList, 5) << ";" << "\n";
+ }
+ t << "\t\t\t\t" << writeSettings("LEXFLAGS", fixListForOutput("QMAKE_LEXFLAGS")) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("YACCFLAGS", fixListForOutput("QMAKE_YACCFLAGS")) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("OTHER_REZFLAGS", QStringList()) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("SECTORDER_FLAGS", QStringList()) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("WARNING_CFLAGS", QStringList()) << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("PREBINDING", QStringList((project->isEmpty("QMAKE_DO_PREBINDING") ? "NO" : "YES")), SettingsNoQuote) << ";" << "\n";
+ if(!project->isEmpty("PRECOMPILED_HEADER")) {
+ if(pbVersion >= 38) {
+ t << "\t\t\t\t" << writeSettings("GCC_PRECOMPILE_PREFIX_HEADER", "YES") << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("GCC_PREFIX_HEADER", escapeFilePath(project->first("PRECOMPILED_HEADER"))) << ";" << "\n";
+ } else {
+ t << "\t\t\t\t" << writeSettings("PRECOMPILE_PREFIX_HEADER", "YES") << ";" << "\n"
+ << "\t\t\t\t" << writeSettings("PREFIX_HEADER", escapeFilePath(project->first("PRECOMPILED_HEADER"))) << ";" << "\n";
+ }
+ }
+ if((project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) ||
+ (project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
+ project->isActiveConfig("lib_bundle"))) {
+ QString plist = fileFixify(project->first("QMAKE_INFO_PLIST"));
+ if(plist.isEmpty())
+ plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE");
+ if(exists(plist)) {
+ QFile plist_in_file(plist);
+ if(plist_in_file.open(QIODevice::ReadOnly)) {
+ QTextStream plist_in(&plist_in_file);
+ QString plist_in_text = plist_in.readAll();
+ plist_in_text = plist_in_text.replace("@ICON@",
+ (project->isEmpty("ICON") ? QString("") : project->first("ICON").section(Option::dir_sep, -1)));
+ if(project->first("TEMPLATE") == "app") {
+ plist_in_text = plist_in_text.replace("@EXECUTABLE@", project->first("QMAKE_ORIG_TARGET"));
+ } else {
+ plist_in_text = plist_in_text.replace("@LIBRARY@", project->first("QMAKE_ORIG_TARGET"));
+ }
+ if (!project->values("VERSION").isEmpty()) {
+ plist_in_text = plist_in_text.replace("@SHORT_VERSION@", project->first("VER_MAJ") + "." +
+ project->first("VER_MIN"));
+ }
+ plist_in_text = plist_in_text.replace("@TYPEINFO@",
+ (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") :
+ project->first("QMAKE_PKGINFO_TYPEINFO").left(4)));
+ QFile plist_out_file("Info.plist");
+ if(plist_out_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QTextStream plist_out(&plist_out_file);
+ plist_out << plist_in_text;
+ t << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", "Info.plist") << ";" << "\n";
+ }
+ }
+ }
+ }
+#if 1
+ t << "\t\t\t\t" << writeSettings("BUILD_ROOT", escapeFilePath(qmake_getpwd())) << ";" << "\n";
+#endif
+ if(!project->isActiveConfig("staticlib")) {
+ t << "\t\t\t\t" << writeSettings("OTHER_LDFLAGS",
+ fixListForOutput("SUBLIBS")
+ + fixListForOutput("QMAKE_LFLAGS")
+ + fixListForOutput("QMAKE_LIBDIR_FLAGS")
+ + fixListForOutput("QMAKE_FRAMEWORKPATH_FLAGS")
+ + fixListForOutput("QMAKE_LIBS"),
+ SettingsAsList, 6) << ";" << "\n";
+ }
+ if(!project->isEmpty("DESTDIR")) {
+ QString dir = project->first("DESTDIR");
+ if (QDir::isRelativePath(dir))
+ dir.prepend(qmake_getpwd() + Option::dir_sep);
+ t << "\t\t\t\t" << writeSettings("INSTALL_DIR", dir) << ";" << "\n";
+ }
+ if (project->first("TEMPLATE") == "lib") {
+ t << "\t\t\t\t" << writeSettings("INSTALL_PATH", QStringList()) << ";" << "\n";
+ }
+ if(!project->isEmpty("VERSION") && project->first("VERSION") != "0.0.0") {
+ t << "\t\t\t\t" << writeSettings("DYLIB_CURRENT_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")+"."+project->first("VER_PAT")) << ";" << "\n";
+ if(project->isEmpty("COMPAT_VERSION"))
+ t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")) << ";" << "\n";
+ if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
+ project->isActiveConfig("lib_bundle"))
+ t << "\t\t\t\t" << writeSettings("FRAMEWORK_VERSION", project->first("QMAKE_FRAMEWORK_VERSION")) << ";" << "\n";
+ }
+ if(!project->isEmpty("COMPAT_FRAMEWORKPATH"))
+ t << "\t\t\t\t" << writeSettings("FRAMEWORK_SEARCH_PATHS", fixListForOutput("QMAKE_FRAMEWORKPATH"), SettingsAsList, 5) << ";" << "\n";
+ if(!project->isEmpty("COMPAT_VERSION"))
+ t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("COMPAT_VERSION")) << ";" << "\n";
+ if(!project->isEmpty("QMAKE_MACOSX_DEPLOYMENT_TARGET"))
+ t << "\t\t\t\t" << writeSettings("MACOSX_DEPLOYMENT_TARGET", project->first("QMAKE_MACOSX_DEPLOYMENT_TARGET")) << ";" << "\n";
+ if(pbVersion >= 38) {
+ if(!project->isEmpty("OBJECTS_DIR"))
+ t << "\t\t\t\t" << writeSettings("OBJROOT", fixForOutput(project->first("OBJECTS_DIR"))) << ";" << "\n";
+ }
+#if 0
+ if(!project->isEmpty("DESTDIR"))
+ t << "\t\t\t\t" << writeSettings("SYMROOT", fixForOutput(project->first("DESTDIR"))) << ";" << "\n";
+ else
+ t << "\t\t\t\t" << writeSettings("SYMROOT", fixForOutput(qmake_getpwd())) << ";" << "\n";
+#endif
+ {
+ QStringList archs;
+ if(project->isActiveConfig("x86"))
+ archs += "i386";
+ if(project->isActiveConfig("ppc")) {
+ if(!archs.isEmpty())
+ archs += " ";
+ archs += "ppc";
+ }
+ if(project->isActiveConfig("ppc64")) {
+ if(!archs.isEmpty())
+ archs += " ";
+ archs += "ppc64";
+ }
+ if(project->isActiveConfig("x86_64")) {
+ if(!archs.isEmpty())
+ archs += " ";
+ archs += "x86_64";
+ }
+ if(!archs.isEmpty())
+ t << "\t\t\t\t" << writeSettings("ARCHS", archs) << ";" << "\n";
+
+ }
+ if(project->first("TEMPLATE") == "app") {
+ if(pbVersion < 38 && project->isActiveConfig("app_bundle"))
+ t << "\t\t\t\t" << writeSettings("WRAPPER_SUFFIX", "app") << ";" << "\n";
+ t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n";
+ } else {
+ if(!project->isActiveConfig("plugin") && project->isActiveConfig("staticlib")) {
+ t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "STATIC") << ";" << "\n";
+ } else {
+ t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "DYNAMIC") << ";" << "\n";
+ }
+ QString lib = project->first("QMAKE_ORIG_TARGET");
+ if(!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib"))
+ lib.prepend("lib");
+ t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", escapeFilePath(lib)) << ";" << "\n";
+ }
+ tmp = project->values("QMAKE_PBX_VARS");
+ for(int i = 0; i < tmp.count(); i++) {
+ QString var = tmp[i], val = qgetenv(var.toLatin1());
+ if(val.isEmpty() && var == "TB")
+ val = "/usr/bin/";
+ t << "\t\t\t\t" << writeSettings(var, escapeFilePath(val)) << ";" << "\n";
+ }
+ t << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << "conditionalBuildSettings = {" << "\n"
+ << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("dependencies", project->values("QMAKE_PBX_TARGET_DEPENDS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("productReference", keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")) << ";" << "\n"
+ << "\t\t\t" << writeSettings("shouldUseHeadermap", "1", SettingsNoQuote) << ";" << "\n";
+ if(pbVersion >= 38)
+ t << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";" << "\n";
+ if(project->first("TEMPLATE") == "app") {
+ if(!project->isActiveConfig("app_bundle")) {
+ if(pbVersion >= 38) {
+ if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE"))
+ t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n";
+ else
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.tool") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("isa", "PBXToolTarget", SettingsNoQuote) << ";" << "\n";
+ }
+ } else {
+ if(pbVersion >= 38) {
+ if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE"))
+ t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n";
+ else
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.application") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("isa", "PBXApplicationTarget", SettingsNoQuote) << ";" << "\n";
+ }
+ t << "\t\t\t" << "productSettingsXML = \"";
+ bool read_plist = false;
+ if(exists("Info.plist")) {
+ QFile plist("Info.plist");
+ if (plist.open(QIODevice::ReadOnly)) {
+ read_plist = true;
+ QTextStream stream(&plist);
+ while(!stream.atEnd())
+ t << stream.readLine().replace('"', "\\\"") << endl;
+ }
+ }
+ if(!read_plist) {
+ t << "<?xml version="
+ << "\\\"1.0\\\" encoding=" << "\\\"UTF-8\\\"" << "?>" << "\n"
+ << "\t\t\t\t" << "<!DOCTYPE plist SYSTEM \\\"file://localhost/System/"
+ << "Library/DTDs/PropertyList.dtd\\\">" << "\n"
+ << "\t\t\t\t" << "<plist version=\\\"0.9\\\">" << "\n"
+ << "\t\t\t\t" << "<dict>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleDevelopmentRegion</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>English</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleExecutable</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>" << project->first("QMAKE_ORIG_TARGET") << "</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleIconFile</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>" << var("ICON").section(Option::dir_sep, -1) << "</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleInfoDictionaryVersion</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>6.0</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundlePackageType</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>APPL</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleSignature</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>"
+ << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") :
+ project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << "</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CFBundleVersion</key>" << "\n"
+ << "\t\t\t\t\t" << "<string>0.1</string>" << "\n"
+ << "\t\t\t\t\t" << "<key>CSResourcesFileMapped</key>" << "\n"
+ << "\t\t\t\t\t" << "<true/>" << "\n"
+ << "\t\t\t\t" << "</dict>" << "\n"
+ << "\t\t\t\t" << "</plist>";
+ }
+ t << "\";" << "\n";
+ }
+ t << "\t\t\t" << writeSettings("name", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n"
+ << "\t\t\t" << writeSettings("productName", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n";
+ } else {
+ QString lib = project->first("QMAKE_ORIG_TARGET");
+ if(!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib"))
+ lib.prepend("lib");
+ t << "\t\t\t" << writeSettings("name", escapeFilePath(lib)) << ";" << "\n"
+ << "\t\t\t" << writeSettings("productName", escapeFilePath(lib)) << ";" << "\n";
+ if(pbVersion >= 38) {
+ if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE"))
+ t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n";
+ else if(project->isActiveConfig("staticlib"))
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.static") << ";" << "\n";
+ else if(project->isActiveConfig("lib_bundle"))
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.framework") << ";" << "\n";
+ else
+ t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.dynamic") << ";" << "\n";
+ } else {
+ t << "\t\t\t" << writeSettings("isa", "PBXLibraryTarget", SettingsNoQuote) << ";" << "\n";
+ }
+ }
+ t << "\t\t\t" << writeSettings("startupPath", "<<ProjectDirectory>>") << ";" << "\n";
+ if(!project->isEmpty("DESTDIR"))
+ t << "\t\t\t" << writeSettings("productInstallPath", escapeFilePath(project->first("DESTDIR"))) << ";" << "\n";
+ t << "\t\t" << "};" << "\n";
+ //DEBUG/RELEASE
+ QString active_buildstyle;
+ for(int as_release = 0; as_release < 2; as_release++)
+ {
+ QMap<QString, QString> settings;
+ settings.insert("COPY_PHASE_STRIP", (as_release ? "YES" : "NO"));
+ settings.insert("GCC_GENERATE_DEBUGGING_SYMBOLS", as_release ? "NO" : "YES");
+ if(!as_release)
+ settings.insert("GCC_OPTIMIZATION_LEVEL", "0");
+ if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK"))
+ settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK"));
+ {
+ const QStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS");
+ for(int i = 0; i < l.size(); ++i) {
+ QString name = l.at(i);
+ const QString value = project->values(name + QLatin1String(".value")).join(QString(Option::field_sep));
+ if(!project->isEmpty(name + QLatin1String(".name")))
+ name = project->values(name + QLatin1String(".name")).first();
+ settings.insert(name, value);
+ }
+ }
+
+ QString name;
+ if(pbVersion >= 42)
+ name = (as_release ? "Release" : "Debug");
+ else
+ name = (as_release ? "Deployment" : "Development");
+ if(pbVersion >= 42) {
+ QString key = keyFor("QMAKE_PBX_BUILDCONFIG_" + name);
+ project->values("QMAKE_PBX_BUILDCONFIGS").append(key);
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n";
+ for(QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
+ t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n";
+ t << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("name", name) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+
+ QString key = keyFor("QMAKE_PBX_BUILDSTYLE_" + name);
+ if(project->isActiveConfig("debug") != (bool)as_release) {
+ project->values("QMAKE_PBX_BUILDSTYLES").append(key);
+ active_buildstyle = name;
+ } else if(pbVersion >= 42) {
+ project->values("QMAKE_PBX_BUILDSTYLES").append(key);
+ }
+ t << "\t\t" << key << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << "buildSettings = {" << "\n";
+ for(QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
+ t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";" << "\n";
+ t << "\t\t\t" << "};" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXBuildStyle") << ";" << "\n"
+ << "\t\t\t" << writeSettings("name", name) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ if(pbVersion >= 42) {
+ t << "\t\t" << keyFor("QMAKE_PBX_BUILDCONFIG_LIST") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("buildConfigurations", project->values("QMAKE_PBX_BUILDCONFIGS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("defaultConfigurationIsName", active_buildstyle) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+ }
+ //ROOT
+ t << "\t\t" << keyFor("QMAKE_PBX_ROOT") << " = {" << "\n"
+ << "\t\t\t" << writeSettings("buildStyles", project->values("QMAKE_PBX_BUILDSTYLES"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t\t" << writeSettings("hasScannedForEncodings", "1", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";" << "\n"
+ << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_PBX_ROOT_GROUP")) << ";" << "\n";
+ if(pbVersion >= 42)
+ t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST")) << ";" << "\n";
+ t << "\t\t\t" << writeSettings("projectDirPath", QStringList()) << ";" << "\n"
+ << "\t\t\t" << writeSettings("targets", project->values("QMAKE_PBX_TARGETS"), SettingsAsList, 4) << ";" << "\n"
+ << "\t\t" << "};" << "\n";
+
+ //FOOTER
+ t << "\t" << "};" << "\n"
+ << "\t" << writeSettings("rootObject", keyFor("QMAKE_PBX_ROOT")) << ";" << "\n"
+ << "}" << endl;
+
+ if(project->isActiveConfig("generate_pbxbuild_makefile")) {
+ QString mkwrap = fileFixify(pbx_dir + Option::dir_sep + ".." + Option::dir_sep + project->first("MAKEFILE"),
+ qmake_getpwd());
+ QFile mkwrapf(mkwrap);
+ if(mkwrapf.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ writingUnixMakefileGenerator = true;
+ debug_msg(1, "pbuilder: Creating file: %s", mkwrap.toLatin1().constData());
+ QTextStream mkwrapt(&mkwrapf);
+ writeHeader(mkwrapt);
+ const char cleans[] = "preprocess_clean ";
+ mkwrapt << "#This is a makefile wrapper for PROJECT BUILDER\n"
+ << "all:" << "\n\t"
+ << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << "\n"
+ << "install: all" << "\n\t"
+ << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << " install\n"
+ << "distclean clean: preprocess_clean" << "\n\t"
+ << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << " clean" << "\n"
+ << (!did_preprocess ? cleans : "") << ":" << "\n";
+ if(did_preprocess)
+ mkwrapt << cleans << ":" << "\n\t"
+ << "make -f "
+ << pbx_dir << Option::dir_sep << "qt_preprocess.mak $@" << endl;
+ writingUnixMakefileGenerator = false;
+ }
+ }
+ return true;
+}
+
+QString
+ProjectBuilderMakefileGenerator::findProgram(const QString &prog)
+{
+ QString ret = prog;
+ if(QDir::isRelativePath(ret)) {
+ QStringList paths = QString(qgetenv("PATH")).split(':');
+ for(int i = 0; i < paths.size(); ++i) {
+ QString path = paths.at(i) + "/" + prog;
+ if(exists(path)) {
+ ret = path;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+QString
+ProjectBuilderMakefileGenerator::fixForOutput(const QString &values)
+{
+ //get the environment variables references
+ QRegExp reg_var("\\$\\((.*)\\)");
+ for(int rep = 0; (rep = reg_var.indexIn(values, rep)) != -1;) {
+ if(project->values("QMAKE_PBX_VARS").indexOf(reg_var.cap(1)) == -1)
+ project->values("QMAKE_PBX_VARS").append(reg_var.cap(1));
+ rep += reg_var.matchedLength();
+ }
+ QString ret = values;
+ ret = ret.replace(QRegExp("\\\\ "), " "); //unescape spaces
+ ret = ret.replace(QRegExp("('|\\\\|\")"), "\\\\1"); //fix quotes
+ ret = ret.replace("\t", " "); //fix tabs
+ ret = ret.replace(QRegExp(" "), "\\ "); //escape spaces
+ return ret;
+}
+
+QStringList
+ProjectBuilderMakefileGenerator::fixListForOutput(const QString &where)
+{
+ QStringList ret;
+ const QStringList &l = project->values(where);
+ for(int i = 0; i < l.count(); i++)
+ ret += fixForOutput(l[i]);
+ return ret;
+}
+
+QString
+ProjectBuilderMakefileGenerator::keyFor(const QString &block)
+{
+#if 1 //This make this code much easier to debug..
+ if(project->isActiveConfig("no_pb_munge_key"))
+ return block;
+#endif
+ QString ret;
+ if(!keys.contains(block)) {
+ ret = qtMD5(block.toUtf8()).left(24).toUpper();
+ keys.insert(block, ret);
+ } else {
+ ret = keys[block];
+ }
+ return ret;
+}
+
+bool
+ProjectBuilderMakefileGenerator::openOutput(QFile &file, const QString &build) const
+{
+ if(QDir::isRelativePath(file.fileName()))
+ file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
+ QFileInfo fi(fileInfo(file.fileName()));
+ if(fi.suffix() != "pbxproj" || file.fileName().isEmpty()) {
+ QString output = file.fileName();
+ if(fi.isDir())
+ output += QDir::separator();
+ if(!output.endsWith(projectSuffix())) {
+ if(file.fileName().isEmpty() || fi.isDir()) {
+ if(project->first("TEMPLATE") == "subdirs" || project->isEmpty("QMAKE_ORIG_TARGET"))
+ output += fileInfo(project->projectFile()).baseName();
+ else
+ output += project->first("QMAKE_ORIG_TARGET");
+ }
+ output += projectSuffix() + QDir::separator();
+ } else if(output[(int)output.length() - 1] != QDir::separator()) {
+ output += QDir::separator();
+ }
+ output += QString("project.pbxproj");
+ output = unescapeFilePath(output);
+ file.setFileName(output);
+ }
+ bool ret = UnixMakefileGenerator::openOutput(file, build);
+ ((ProjectBuilderMakefileGenerator*)this)->pbx_dir = Option::output_dir.section(Option::dir_sep, 0, -1);
+ Option::output_dir = pbx_dir.section(Option::dir_sep, 0, -2);
+ return ret;
+}
+
+/* This function is such a hack it is almost pointless, but it
+ eliminates the warning message from ProjectBuilder that the project
+ file is for an older version. I guess this could be used someday if
+ the format of the output is dependant upon the version of
+ ProjectBuilder as well.
+*/
+int
+ProjectBuilderMakefileGenerator::pbuilderVersion() const
+{
+ QString ret;
+ if(!project->isEmpty("QMAKE_PBUILDER_VERSION")) {
+ ret = project->first("QMAKE_PBUILDER_VERSION");
+ } else {
+ QString version, version_plist = project->first("QMAKE_PBUILDER_VERSION_PLIST");
+ if(version_plist.isEmpty()) {
+#ifdef Q_OS_DARWIN
+ ret = QLatin1String("34");
+ QCFType<CFURLRef> cfurl;
+ OSStatus err = LSFindApplicationForInfo(0, CFSTR("com.apple.Xcode"), 0, 0, &cfurl);
+ if (err == noErr) {
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);
+ if (bundle) {
+ CFStringRef str = CFStringRef(CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("CFBundleShortVersionString")));
+ if (str) {
+ QStringList versions = QCFString::toQString(str).split(QLatin1Char('.'));
+ int versionMajor = versions.at(0).toInt();
+ int versionMinor = versions.at(1).toInt();
+ if (versionMajor >= 2) {
+ ret = QLatin1String("42");
+ } else if (versionMajor == 1 && versionMinor >= 5) {
+ ret = QLatin1String("39");
+ }
+ }
+ }
+ }
+#else
+ if(exists("/Developer/Applications/Xcode.app/Contents/version.plist"))
+ version_plist = "/Developer/Applications/Xcode.app/Contents/version.plist";
+ else
+ version_plist = "/Developer/Applications/Project Builder.app/Contents/version.plist";
+#endif
+ } else {
+ version_plist = version_plist.replace(QRegExp("\""), "");
+ }
+ if (ret.isEmpty()) {
+ QFile version_file(version_plist);
+ if (version_file.open(QIODevice::ReadOnly)) {
+ debug_msg(1, "pbuilder: version.plist: Reading file: %s", version_plist.toLatin1().constData());
+ QTextStream plist(&version_file);
+
+ bool in_dict = false;
+ QString current_key;
+ QRegExp keyreg("^<key>(.*)</key>$"), stringreg("^<string>(.*)</string>$");
+ while(!plist.atEnd()) {
+ QString line = plist.readLine().trimmed();
+ if(line == "<dict>")
+ in_dict = true;
+ else if(line == "</dict>")
+ in_dict = false;
+ else if(in_dict) {
+ if(keyreg.exactMatch(line))
+ current_key = keyreg.cap(1);
+ else if(current_key == "CFBundleShortVersionString" && stringreg.exactMatch(line))
+ version = stringreg.cap(1);
+ }
+ }
+ plist.flush();
+ version_file.close();
+ } else {
+ debug_msg(1, "pbuilder: version.plist: Failure to open %s", version_plist.toLatin1().constData());
+ }
+ if(version.isEmpty() && version_plist.contains("Xcode")) {
+ ret = "39";
+ } else {
+ int versionMajor = version.left(1).toInt();
+ if(versionMajor >= 2)
+ ret = "42";
+ else if(version == "1.5")
+ ret = "39";
+ else if(version == "1.1")
+ ret = "34";
+ }
+ }
+ }
+
+ if(!ret.isEmpty()) {
+ bool ok;
+ int int_ret = ret.toInt(&ok);
+ if(ok) {
+ debug_msg(1, "pbuilder: version.plist: Got version: %d", int_ret);
+ return int_ret;
+ }
+ }
+ debug_msg(1, "pbuilder: version.plist: Fallback to default version");
+ return 42; //my fallback
+}
+
+int
+ProjectBuilderMakefileGenerator::reftypeForFile(const QString &where)
+{
+ int ret = 0; //absolute is the default..
+ if(QDir::isRelativePath(unescapeFilePath(where)))
+ ret = 4; //relative
+ return ret;
+}
+
+QString
+ProjectBuilderMakefileGenerator::projectSuffix() const
+{
+ const int pbVersion = pbuilderVersion();
+ if(pbVersion >= 42)
+ return ".xcodeproj";
+ else if(pbVersion >= 38)
+ return ".xcode";
+ return ".pbproj";
+}
+
+QString
+ProjectBuilderMakefileGenerator::pbxbuild()
+{
+ if(exists("/usr/bin/pbbuild"))
+ return "pbbuild";
+ if(exists("/usr/bin/xcodebuild"))
+ return "xcodebuild";
+ return (pbuilderVersion() >= 38 ? "xcodebuild" : "pbxbuild");
+}
+
+QString
+ProjectBuilderMakefileGenerator::escapeFilePath(const QString &path) const
+{
+#if 1
+ //in the middle of generating a Makefile!
+ if(writingUnixMakefileGenerator)
+ return UnixMakefileGenerator::escapeFilePath(path);
+
+ //generating stuff for the xml file!
+ QString ret = path;
+ if(!ret.isEmpty()) {
+ ret = unescapeFilePath(ret);
+ debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
+ }
+ return ret;
+#else
+ return UnixMakefileGenerator::escapeFilePath(path);
+#endif
+}
+
+QString
+ProjectBuilderMakefileGenerator::writeSettings(QString var, QStringList vals, int flags, int indent_level)
+{
+ QString ret;
+ const QString quote = (flags & SettingsNoQuote) ? "" : "\"";
+ const QString escape_quote = quote.isEmpty() ? "" : "\\" + quote;
+ QString newline = "\n";
+ for(int i = 0; i < indent_level; ++i)
+ newline += "\t";
+ if(flags & SettingsAsList) {
+ ret += var + " = (" + newline;
+ for(int i = 0, count = 0; i < vals.size(); ++i) {
+ QString val = vals.at(i);
+ if(!val.isEmpty()) {
+ if(count++ > 0)
+ ret += "," + newline;
+ ret += quote + val.replace(quote, escape_quote) + quote;
+ }
+ }
+ ret += ")";
+ } else {
+ ret += var + " = " + quote;
+ for(int i = 0; i < vals.size(); ++i) {
+ QString val = vals.at(i);
+// if(val.isEmpty())
+// val = quote + quote;
+ if(i)
+ ret += " ";
+ ret += val;
+ }
+ ret += quote;
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/mac/pbuilder_pbx.h b/qmake/generators/mac/pbuilder_pbx.h
new file mode 100644
index 0000000000..c11425a141
--- /dev/null
+++ b/qmake/generators/mac/pbuilder_pbx.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PBUILDER_PBX_H
+#define PBUILDER_PBX_H
+
+#include "unixmake.h"
+
+QT_BEGIN_NAMESPACE
+
+class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator
+{
+ bool writingUnixMakefileGenerator;
+ QString pbx_dir;
+ int pbuilderVersion() const;
+ bool writeSubDirs(QTextStream &);
+ bool writeMakeParts(QTextStream &);
+ bool writeMakefile(QTextStream &);
+
+ QString pbxbuild();
+ QMap<QString, QString> keys;
+ QString keyFor(const QString &file);
+ QString findProgram(const QString &prog);
+ QString fixForOutput(const QString &file);
+ QStringList fixListForOutput(const QString &where);
+ int reftypeForFile(const QString &where);
+ QString projectSuffix() const;
+ enum { SettingsAsList=0x01, SettingsNoQuote=0x02 };
+ inline QString writeSettings(QString var, QString val, int flags=0, int indent_level=0)
+ { Q_UNUSED(indent_level); return writeSettings(var, QStringList(val), flags); }
+ QString writeSettings(QString var, QStringList vals, int flags=0, int indent_level=0);
+
+public:
+ ProjectBuilderMakefileGenerator();
+ ~ProjectBuilderMakefileGenerator();
+
+ virtual bool supportsMetaBuild() { return false; }
+ virtual bool openOutput(QFile &, const QString &) const;
+protected:
+ virtual QString escapeFilePath(const QString &path) const;
+ bool doPrecompiledHeaders() const { return false; }
+ virtual bool doDepends() const { return false; } //never necesary
+};
+
+inline ProjectBuilderMakefileGenerator::~ProjectBuilderMakefileGenerator()
+{ }
+
+QT_END_NAMESPACE
+
+#endif // PBUILDER_PBX_H
diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp
new file mode 100644
index 0000000000..4f3b1137f5
--- /dev/null
+++ b/qmake/generators/makefile.cpp
@@ -0,0 +1,3297 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "makefile.h"
+#include "option.h"
+#include "cachekeys.h"
+#include "meta.h"
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qhash.h>
+#include <qdebug.h>
+#include <qbuffer.h>
+#include <qsettings.h>
+#include <qdatetime.h>
+#if defined(Q_OS_UNIX)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+#include <qdebug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+QT_BEGIN_NAMESPACE
+
+// Well, Windows doesn't have this, so here's the macro
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const
+{
+ int argv0 = -1;
+ for(int i = 0; i < cmdline.count(); ++i) {
+ if(!cmdline.at(i).contains('=')) {
+ argv0 = i;
+ break;
+ }
+ }
+ if(a)
+ *a = argv0;
+ if(argv0 != -1) {
+ const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
+ if(exists(c))
+ return true;
+ }
+ return false;
+}
+
+QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const
+{
+ QString ret = "@$(CHK_DIR_EXISTS) ";
+ if(escape)
+ ret += escapeFilePath(dir);
+ else
+ ret += dir;
+ ret += " ";
+ if(isWindowsShell())
+ ret += "$(MKDIR)";
+ else
+ ret += "|| $(MKDIR)";
+ ret += " ";
+ if(escape)
+ ret += escapeFilePath(dir);
+ else
+ ret += dir;
+ ret += " ";
+ return ret;
+}
+
+bool MakefileGenerator::mkdir(const QString &in_path) const
+{
+ QString path = Option::fixPathToLocalOS(in_path);
+ if(QFile::exists(path))
+ return true;
+
+ QDir d;
+ if(path.startsWith(QDir::separator())) {
+ d.cd(QString(QDir::separator()));
+ path.remove(0, 1);
+ }
+ bool ret = true;
+#ifdef Q_OS_WIN
+ bool driveExists = true;
+ if(!QDir::isRelativePath(path)) {
+ if(QFile::exists(path.left(3))) {
+ d.cd(path.left(3));
+ path.remove(0, 3);
+ } else {
+ warn_msg(WarnLogic, "Cannot access drive '%s' (%s)",
+ path.left(3).toLatin1().data(), path.toLatin1().data());
+ driveExists = false;
+ }
+ }
+ if(driveExists)
+#endif
+ {
+ QStringList subs = path.split(QDir::separator());
+ for(QStringList::Iterator subit = subs.begin(); subit != subs.end(); ++subit) {
+ if(!d.cd(*subit)) {
+ d.mkdir((*subit));
+ if(d.exists((*subit))) {
+ d.cd((*subit));
+ } else {
+ ret = false;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+// ** base makefile generator
+MakefileGenerator::MakefileGenerator() :
+ init_opath_already(false), init_already(false), no_io(false), project(0)
+{
+}
+
+
+void
+MakefileGenerator::verifyCompilers()
+{
+ QMap<QString, QStringList> &v = project->variables();
+ QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
+ for(int i = 0; i < quc.size(); ) {
+ bool error = false;
+ QString comp = quc.at(i);
+ if(v[comp + ".output"].isEmpty()) {
+ if(!v[comp + ".output_function"].isEmpty()) {
+ v[comp + ".output"].append("${QMAKE_FUNC_FILE_IN_" + v[comp + ".output_function"].first() + "}");
+ } else {
+ error = true;
+ warn_msg(WarnLogic, "Compiler: %s: No output file specified", comp.toLatin1().constData());
+ }
+ } else if(v[comp + ".input"].isEmpty()) {
+ error = true;
+ warn_msg(WarnLogic, "Compiler: %s: No input variable specified", comp.toLatin1().constData());
+ }
+ if(error)
+ quc.removeAt(i);
+ else
+ ++i;
+ }
+}
+
+void
+MakefileGenerator::initOutPaths()
+{
+ if(init_opath_already)
+ return;
+ verifyCompilers();
+ init_opath_already = true;
+ QMap<QString, QStringList> &v = project->variables();
+ //for shadow builds
+ if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) {
+ if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty() &&
+ v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) {
+ QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first();
+ root = QDir::fromNativeSeparators(root);
+ if(!root.isEmpty()) {
+ QFileInfo fi = fileInfo(Option::mkfile::cachefile);
+ if(!fi.makeAbsolute()) {
+ QString cache_r = fi.path(), pwd = Option::output_dir;
+ if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) {
+ pwd = root + pwd.mid(cache_r.length());
+ if(exists(pwd))
+ v.insert("QMAKE_ABSOLUTE_SOURCE_PATH", QStringList(pwd));
+ }
+ }
+ }
+ }
+ }
+ if(!v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) {
+ QString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first();
+ asp = QDir::fromNativeSeparators(asp);
+ if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother?
+ v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear();
+ }
+
+ QString currentDir = qmake_getpwd(); //just to go back to
+
+ //some builtin directories
+ if(project->isEmpty("PRECOMPILED_DIR") && !project->isEmpty("OBJECTS_DIR"))
+ v["PRECOMPILED_DIR"] = v["OBJECTS_DIR"];
+ QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
+ QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
+ QString("PRECOMPILED_DIR"), QString() };
+ for(int x = 0; !dirs[x].isEmpty(); x++) {
+ if(v[dirs[x]].isEmpty())
+ continue;
+ const QString orig_path = v[dirs[x]].first();
+
+ QString &pathRef = v[dirs[x]].first();
+ pathRef = fileFixify(pathRef, Option::output_dir, Option::output_dir);
+
+#ifdef Q_OS_WIN
+ // We don't want to add a separator for DLLDESTDIR on Windows (###why?)
+ if(!(dirs[x] == "DLLDESTDIR"))
+#endif
+ {
+ if(!pathRef.endsWith(Option::dir_sep))
+ pathRef += Option::dir_sep;
+ }
+
+ if(noIO())
+ continue;
+
+ QString path = project->first(dirs[x]); //not to be changed any further
+ path = fileFixify(path, currentDir, Option::output_dir);
+ debug_msg(3, "Fixed output_dir %s (%s) into %s", dirs[x].toLatin1().constData(),
+ orig_path.toLatin1().constData(), path.toLatin1().constData());
+ if(!mkdir(path))
+ warn_msg(WarnLogic, "%s: Cannot access directory '%s'", dirs[x].toLatin1().constData(),
+ path.toLatin1().constData());
+ }
+
+ //out paths from the extra compilers
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = project->values((*it) + ".output").first();
+ if(tmp_out.isEmpty())
+ continue;
+ const QStringList &tmp = project->values((*it) + ".input");
+ for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
+ QStringList &inputs = project->values((*it2));
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
+ (*input) = fileFixify((*input), Option::output_dir, Option::output_dir);
+ QString path = unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
+ path = Option::fixPathToTargetOS(path);
+ int slash = path.lastIndexOf(Option::dir_sep);
+ if(slash != -1) {
+ path = path.left(slash);
+ // Make out path only if it does not contain makefile variables
+ if(!path.contains("${"))
+ if(path != "." &&
+ !mkdir(fileFixify(path, qmake_getpwd(), Option::output_dir)))
+ warn_msg(WarnLogic, "%s: Cannot access directory '%s'",
+ (*it).toLatin1().constData(), path.toLatin1().constData());
+ }
+ }
+ }
+ }
+
+ if(!v["DESTDIR"].isEmpty()) {
+ QDir d(v["DESTDIR"].first());
+ if(Option::fixPathToLocalOS(d.absolutePath()) == Option::fixPathToLocalOS(Option::output_dir))
+ v.remove("DESTDIR");
+ }
+}
+
+QMakeProject
+*MakefileGenerator::projectFile() const
+{
+ return project;
+}
+
+void
+MakefileGenerator::setProjectFile(QMakeProject *p)
+{
+ if(project)
+ return;
+ project = p;
+ init();
+ usePlatformDir();
+ findLibraries();
+ if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE &&
+ project->isActiveConfig("link_prl")) //load up prl's'
+ processPrlFiles();
+}
+
+QStringList
+MakefileGenerator::findFilesInVPATH(QStringList l, uchar flags, const QString &vpath_var)
+{
+ QStringList vpath;
+ QMap<QString, QStringList> &v = project->variables();
+ for(int val_it = 0; val_it < l.count(); ) {
+ bool remove_file = false;
+ QString &val = l[val_it];
+ if(!val.isEmpty()) {
+ QString file = fixEnvVariables(val);
+ if(!(flags & VPATH_NoFixify))
+ file = fileFixify(file, qmake_getpwd(), Option::output_dir);
+ if (file.at(0) == '\"' && file.at(file.length() - 1) == '\"')
+ file = file.mid(1, file.length() - 2);
+
+ if(exists(file)) {
+ ++val_it;
+ continue;
+ }
+ bool found = false;
+ if(QDir::isRelativePath(val)) {
+ if(vpath.isEmpty()) {
+ if(!vpath_var.isEmpty())
+ vpath = v[vpath_var];
+ vpath += v["VPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"] + v["DEPENDPATH"];
+ if(Option::output_dir != qmake_getpwd())
+ vpath += Option::output_dir;
+ }
+ for(QStringList::Iterator vpath_it = vpath.begin();
+ vpath_it != vpath.end(); ++vpath_it) {
+ QString real_dir = Option::fixPathToLocalOS((*vpath_it));
+ if(exists(real_dir + QDir::separator() + val)) {
+ QString dir = (*vpath_it);
+ if(!dir.endsWith(Option::dir_sep))
+ dir += Option::dir_sep;
+ val = dir + val;
+ if(!(flags & VPATH_NoFixify))
+ val = fileFixify(val);
+ found = true;
+ debug_msg(1, "Found file through vpath %s -> %s",
+ file.toLatin1().constData(), val.toLatin1().constData());
+ break;
+ }
+ }
+ }
+ if(!found) {
+ QString dir, regex = val, real_dir;
+ if(regex.lastIndexOf(Option::dir_sep) != -1) {
+ dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
+ real_dir = dir;
+ if(!(flags & VPATH_NoFixify))
+ real_dir = fileFixify(real_dir, qmake_getpwd(), Option::output_dir) + '/';
+ regex.remove(0, dir.length());
+ }
+ if(real_dir.isEmpty() || exists(real_dir)) {
+ QStringList files = QDir(real_dir).entryList(QStringList(regex));
+ if(files.isEmpty()) {
+ debug_msg(1, "%s:%d Failure to find %s in vpath (%s)",
+ __FILE__, __LINE__,
+ val.toLatin1().constData(), vpath.join("::").toLatin1().constData());
+ if(flags & VPATH_RemoveMissingFiles)
+ remove_file = true;
+ else if(flags & VPATH_WarnMissingFiles)
+ warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
+ } else {
+ l.removeAt(val_it);
+ QString a;
+ for(int i = (int)files.count()-1; i >= 0; i--) {
+ if(files[i] == "." || files[i] == "..")
+ continue;
+ a = real_dir + files[i];
+ if(!(flags & VPATH_NoFixify))
+ a = fileFixify(a);
+ l.insert(val_it, a);
+ }
+ }
+ } else {
+ debug_msg(1, "%s:%d Cannot match %s%s, as %s does not exist.",
+ __FILE__, __LINE__, real_dir.toLatin1().constData(),
+ regex.toLatin1().constData(), real_dir.toLatin1().constData());
+ if(flags & VPATH_RemoveMissingFiles)
+ remove_file = true;
+ else if(flags & VPATH_WarnMissingFiles)
+ warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
+ }
+ }
+ }
+ if(remove_file)
+ l.removeAt(val_it);
+ else
+ ++val_it;
+ }
+ return l;
+}
+
+void
+MakefileGenerator::initCompiler(const MakefileGenerator::Compiler &comp)
+{
+ QMap<QString, QStringList> &v = project->variables();
+ QStringList &l = v[comp.variable_in];
+ // find all the relevant file inputs
+ if(!init_compiler_already.contains(comp.variable_in)) {
+ init_compiler_already.insert(comp.variable_in, true);
+ if(!noIO())
+ l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ?
+ VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in);
+ }
+}
+
+void
+MakefileGenerator::init()
+{
+ initOutPaths();
+ if(init_already)
+ return;
+ verifyCompilers();
+ init_already = true;
+
+ QMap<QString, QStringList> &v = project->variables();
+ QStringList &quc = v["QMAKE_EXTRA_COMPILERS"];
+
+ //make sure the COMPILERS are in the correct input/output chain order
+ for(int comp_out = 0, jump_count = 0; comp_out < quc.size(); ++comp_out) {
+ continue_compiler_chain:
+ if(jump_count > quc.size()) //just to avoid an infinite loop here
+ break;
+ if(project->variables().contains(quc.at(comp_out) + ".variable_out")) {
+ const QStringList &outputs = project->variables().value(quc.at(comp_out) + ".variable_out");
+ for(int out = 0; out < outputs.size(); ++out) {
+ for(int comp_in = 0; comp_in < quc.size(); ++comp_in) {
+ if(comp_in == comp_out)
+ continue;
+ if(project->variables().contains(quc.at(comp_in) + ".input")) {
+ const QStringList &inputs = project->variables().value(quc.at(comp_in) + ".input");
+ for(int in = 0; in < inputs.size(); ++in) {
+ if(inputs.at(in) == outputs.at(out) && comp_out > comp_in) {
+ ++jump_count;
+ //move comp_out to comp_in and continue the compiler chain
+ quc.move(comp_out, comp_in);
+ comp_out = comp_in;
+ goto continue_compiler_chain;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!project->isEmpty("QMAKE_SUBSTITUTES")) {
+ const QStringList &subs = v["QMAKE_SUBSTITUTES"];
+ for(int i = 0; i < subs.size(); ++i) {
+ QString inn = subs.at(i) + ".input", outn = subs.at(i) + ".output";
+ if (v.contains(inn) || v.contains(outn)) {
+ if (!v.contains(inn) || !v.contains(outn)) {
+ warn_msg(WarnLogic, "Substitute '%s' has only one of .input and .output",
+ subs.at(i).toLatin1().constData());
+ continue;
+ }
+ const QStringList &tinn = v[inn], &toutn = v[outn];
+ if (tinn.length() != 1) {
+ warn_msg(WarnLogic, "Substitute '%s.input' does not have exactly one value",
+ subs.at(i).toLatin1().constData());
+ continue;
+ }
+ if (toutn.length() != 1) {
+ warn_msg(WarnLogic, "Substitute '%s.output' does not have exactly one value",
+ subs.at(i).toLatin1().constData());
+ continue;
+ }
+ inn = fileFixify(tinn.first(), qmake_getpwd());
+ outn = fileFixify(toutn.first(), qmake_getpwd(), Option::output_dir);
+ } else {
+ inn = fileFixify(subs.at(i), qmake_getpwd());
+ if (!QFile::exists(inn)) {
+ // random insanity for backwards compat: .in file specified with absolute out dir
+ inn = fileFixify(subs.at(i));
+ }
+ if(!inn.endsWith(".in")) {
+ warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'",
+ inn.toLatin1().constData());
+ continue;
+ }
+ outn = fileFixify(inn.left(inn.length()-3), qmake_getpwd(), Option::output_dir);
+ }
+ QFile in(inn);
+ if(in.open(QFile::ReadOnly)) {
+ QString contents;
+ QStack<int> state;
+ enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
+ for(int count = 1; !in.atEnd(); ++count) {
+ QString line = QString::fromUtf8(in.readLine());
+ if(line.startsWith("!!IF ")) {
+ if(state.isEmpty() || state.top() == IN_CONDITION) {
+ QString test = line.mid(5, line.length()-(5+1));
+ if(project->test(test))
+ state.push(IN_CONDITION);
+ else
+ state.push(PENDING_CONDITION);
+ } else {
+ state.push(MET_CONDITION);
+ }
+ } else if(line.startsWith("!!ELIF ")) {
+ if(state.isEmpty()) {
+ warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
+ in.fileName().toLatin1().constData(), count);
+ } else if(state.top() == PENDING_CONDITION) {
+ QString test = line.mid(7, line.length()-(7+1));
+ if(project->test(test)) {
+ state.pop();
+ state.push(IN_CONDITION);
+ }
+ } else if(state.top() == IN_CONDITION) {
+ state.pop();
+ state.push(MET_CONDITION);
+ }
+ } else if(line.startsWith("!!ELSE")) {
+ if(state.isEmpty()) {
+ warn_msg(WarnLogic, "(%s:%d): Unexpected else condition",
+ in.fileName().toLatin1().constData(), count);
+ } else if(state.top() == PENDING_CONDITION) {
+ state.pop();
+ state.push(IN_CONDITION);
+ } else if(state.top() == IN_CONDITION) {
+ state.pop();
+ state.push(MET_CONDITION);
+ }
+ } else if(line.startsWith("!!ENDIF")) {
+ if(state.isEmpty())
+ warn_msg(WarnLogic, "(%s:%d): Unexpected endif",
+ in.fileName().toLatin1().constData(), count);
+ else
+ state.pop();
+ } else if(state.isEmpty() || state.top() == IN_CONDITION) {
+ contents += project->expand(line, in.fileName(), count);
+ }
+ }
+ QFile out(outn);
+ if(out.exists() && out.open(QFile::ReadOnly)) {
+ QString old = QString::fromUtf8(out.readAll());
+ if(contents == old) {
+ v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
+ continue;
+ }
+ out.close();
+ if(!out.remove()) {
+ warn_msg(WarnLogic, "Cannot clear substitute '%s'",
+ out.fileName().toLatin1().constData());
+ continue;
+ }
+ }
+ mkdir(QFileInfo(out).absolutePath());
+ if(out.open(QFile::WriteOnly)) {
+ v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName());
+ out.write(contents.toUtf8());
+ } else {
+ warn_msg(WarnLogic, "Cannot open substitute for output '%s'",
+ out.fileName().toLatin1().constData());
+ }
+ } else {
+ warn_msg(WarnLogic, "Cannot open substitute for input '%s'",
+ in.fileName().toLatin1().constData());
+ }
+ }
+ }
+
+ int x;
+
+ //build up a list of compilers
+ QList<Compiler> compilers;
+ {
+ const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", 0 };
+ for(x = 0; builtins[x]; ++x) {
+ Compiler compiler;
+ compiler.variable_in = builtins[x];
+ compiler.flags = Compiler::CompilerBuiltin;
+ compiler.type = QMakeSourceFileInfo::TYPE_C;
+ if(!strcmp(builtins[x], "OBJECTS"))
+ compiler.flags |= Compiler::CompilerNoCheckDeps;
+ compilers.append(compiler);
+ }
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ const QStringList &inputs = v[(*it) + ".input"];
+ for(x = 0; x < inputs.size(); ++x) {
+ Compiler compiler;
+ compiler.variable_in = inputs.at(x);
+ compiler.flags = Compiler::CompilerNoFlags;
+ if(v[(*it) + ".CONFIG"].indexOf("ignore_no_exist") != -1)
+ compiler.flags |= Compiler::CompilerRemoveNoExist;
+ if(v[(*it) + ".CONFIG"].indexOf("no_dependencies") != -1)
+ compiler.flags |= Compiler::CompilerNoCheckDeps;
+
+ QString dep_type;
+ if(!project->isEmpty((*it) + ".dependency_type"))
+ dep_type = project->first((*it) + ".dependency_type");
+ if (dep_type.isEmpty())
+ compiler.type = QMakeSourceFileInfo::TYPE_UNKNOWN;
+ else if(dep_type == "TYPE_UI")
+ compiler.type = QMakeSourceFileInfo::TYPE_UI;
+ else
+ compiler.type = QMakeSourceFileInfo::TYPE_C;
+ compilers.append(compiler);
+ }
+ }
+ }
+ { //do the path fixifying
+ QStringList paths;
+ for(x = 0; x < compilers.count(); ++x) {
+ if(!paths.contains(compilers.at(x).variable_in))
+ paths << compilers.at(x).variable_in;
+ }
+ paths << "INCLUDEPATH" << "QMAKE_INTERNAL_INCLUDED_FILES" << "PRECOMPILED_HEADER";
+ for(int y = 0; y < paths.count(); y++) {
+ QStringList &l = v[paths[y]];
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if((*it).isEmpty())
+ continue;
+ if(exists((*it)))
+ (*it) = fileFixify((*it));
+ }
+ }
+ }
+
+ if(noIO() || !doDepends())
+ QMakeSourceFileInfo::setDependencyMode(QMakeSourceFileInfo::NonRecursive);
+ for(x = 0; x < compilers.count(); ++x)
+ initCompiler(compilers.at(x));
+
+ //merge actual compiler outputs into their variable_out. This is done last so that
+ //files are already properly fixified.
+ for(QStringList::Iterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = project->values((*it) + ".output").first();
+ if(tmp_out.isEmpty())
+ continue;
+ if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
+ QStringList &compilerInputs = project->values((*it) + ".input");
+ // Don't generate compiler output if it doesn't have input.
+ if (compilerInputs.isEmpty() || project->values(compilerInputs.first()).isEmpty())
+ continue;
+ if(tmp_out.indexOf("$") == -1) {
+ if(!verifyExtraCompiler((*it), QString())) //verify
+ continue;
+ QString out = fileFixify(tmp_out, Option::output_dir, Option::output_dir);
+ bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
+ if(project->variables().contains((*it) + ".variable_out")) {
+ const QStringList &var_out = project->variables().value((*it) + ".variable_out");
+ for(int i = 0; i < var_out.size(); ++i) {
+ QString v = var_out.at(i);
+ if(v == QLatin1String("SOURCES"))
+ v = "GENERATED_SOURCES";
+ else if(v == QLatin1String("OBJECTS"))
+ pre_dep = false;
+ QStringList &list = project->values(v);
+ if(!list.contains(out))
+ list.append(out);
+ }
+ } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
+ QStringList &list = project->values("OBJECTS");
+ pre_dep = false;
+ if(!list.contains(out))
+ list.append(out);
+ } else {
+ QStringList &list = project->values("UNUSED_SOURCES");
+ if(!list.contains(out))
+ list.append(out);
+ }
+ if(pre_dep) {
+ QStringList &list = project->variables()["PRE_TARGETDEPS"];
+ if(!list.contains(out))
+ list.append(out);
+ }
+ }
+ } else {
+ QStringList &tmp = project->values((*it) + ".input");
+ for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
+ const QStringList inputs = project->values((*it2));
+ for(QStringList::ConstIterator input = inputs.constBegin(); input != inputs.constEnd(); ++input) {
+ if((*input).isEmpty())
+ continue;
+ QString in = Option::fixPathToTargetOS((*input), false);
+ if(!verifyExtraCompiler((*it), in)) //verify
+ continue;
+ QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
+ out = fileFixify(out, Option::output_dir, Option::output_dir);
+ bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1);
+ if(project->variables().contains((*it) + ".variable_out")) {
+ const QStringList &var_out = project->variables().value((*it) + ".variable_out");
+ for(int i = 0; i < var_out.size(); ++i) {
+ QString v = var_out.at(i);
+ if(v == QLatin1String("SOURCES"))
+ v = "GENERATED_SOURCES";
+ else if(v == QLatin1String("OBJECTS"))
+ pre_dep = false;
+ QStringList &list = project->values(v);
+ if(!list.contains(out))
+ list.append(out);
+ }
+ } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) {
+ pre_dep = false;
+ QStringList &list = project->values("OBJECTS");
+ if(!list.contains(out))
+ list.append(out);
+ } else {
+ QStringList &list = project->values("UNUSED_SOURCES");
+ if(!list.contains(out))
+ list.append(out);
+ }
+ if(pre_dep) {
+ QStringList &list = project->variables()["PRE_TARGETDEPS"];
+ if(!list.contains(out))
+ list.append(out);
+ }
+ }
+ }
+ }
+ }
+
+ //handle dependencies
+ depHeuristicsCache.clear();
+ if(!noIO()) {
+ // dependency paths
+ QStringList incDirs = v["DEPENDPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"];
+ if(project->isActiveConfig("depend_includepath"))
+ incDirs += v["INCLUDEPATH"];
+ if(!project->isActiveConfig("no_include_pwd")) {
+ QString pwd = qmake_getpwd();
+ if(pwd.isEmpty())
+ pwd = ".";
+ incDirs += pwd;
+ }
+ QList<QMakeLocalFileName> deplist;
+ for(QStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it)
+ deplist.append(QMakeLocalFileName(unescapeFilePath((*it))));
+ QMakeSourceFileInfo::setDependencyPaths(deplist);
+ debug_msg(1, "Dependency Directories: %s", incDirs.join(" :: ").toLatin1().constData());
+ //cache info
+ if(project->isActiveConfig("qmake_cache")) {
+ QString cache_file;
+ if(!project->isEmpty("QMAKE_INTERNAL_CACHE_FILE")) {
+ cache_file = QDir::fromNativeSeparators(project->first("QMAKE_INTERNAL_CACHE_FILE"));
+ } else {
+ cache_file = ".qmake.internal.cache";
+ if(project->isActiveConfig("build_pass"))
+ cache_file += ".BUILD." + project->first("BUILD_PASS");
+ }
+ if(cache_file.indexOf('/') == -1)
+ cache_file.prepend(Option::output_dir + '/');
+ QMakeSourceFileInfo::setCacheFile(cache_file);
+ }
+
+ //add to dependency engine
+ for(x = 0; x < compilers.count(); ++x) {
+ const MakefileGenerator::Compiler &comp = compilers.at(x);
+ if(!(comp.flags & Compiler::CompilerNoCheckDeps))
+ addSourceFiles(v[comp.variable_in], QMakeSourceFileInfo::SEEK_DEPS,
+ (QMakeSourceFileInfo::SourceFileType)comp.type);
+ }
+ }
+
+ processSources(); //remove anything in SOURCES which is included (thus it need not be linked in)
+
+ //all sources and generated sources must be turned into objects at some point (the one builtin compiler)
+ v["OBJECTS"] += createObjectList(v["SOURCES"]) + createObjectList(v["GENERATED_SOURCES"]);
+
+ //Translation files
+ if(!project->isEmpty("TRANSLATIONS")) {
+ QStringList &trf = project->values("TRANSLATIONS");
+ for(QStringList::Iterator it = trf.begin(); it != trf.end(); ++it)
+ (*it) = Option::fixPathToLocalOS((*it));
+ }
+
+ { //get the output_dir into the pwd
+ if(Option::output_dir != qmake_getpwd())
+ project->values("INCLUDEPATH").append(".");
+ }
+
+ //fix up the target deps
+ QString fixpaths[] = { QString("PRE_TARGETDEPS"), QString("POST_TARGETDEPS"), QString() };
+ for(int path = 0; !fixpaths[path].isNull(); path++) {
+ QStringList &l = v[fixpaths[path]];
+ for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
+ if(!(*val_it).isEmpty())
+ (*val_it) = escapeDependencyPath(Option::fixPathToTargetOS((*val_it), false, false));
+ }
+ }
+
+ //extra depends
+ if(!project->isEmpty("DEPENDS")) {
+ QStringList &l = v["DEPENDS"];
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QStringList files = v[(*it) + ".file"] + v[(*it) + ".files"]; //why do I support such evil things?
+ for(QStringList::Iterator file_it = files.begin(); file_it != files.end(); ++file_it) {
+ QStringList &out_deps = findDependencies(*file_it);
+ QStringList &in_deps = v[(*it) + ".depends"]; //even more evilness..
+ for(QStringList::Iterator dep_it = in_deps.begin(); dep_it != in_deps.end(); ++dep_it) {
+ if(exists(*dep_it)) {
+ out_deps.append(*dep_it);
+ } else {
+ QString dir, regex = Option::fixPathToLocalOS((*dep_it));
+ if(regex.lastIndexOf(Option::dir_sep) != -1) {
+ dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1);
+ regex.remove(0, dir.length());
+ }
+ QStringList files = QDir(dir).entryList(QStringList(regex));
+ if(files.isEmpty()) {
+ warn_msg(WarnLogic, "Dependency for [%s]: Not found %s", (*file_it).toLatin1().constData(),
+ (*dep_it).toLatin1().constData());
+ } else {
+ for(int i = 0; i < files.count(); i++)
+ out_deps.append(dir + files[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // escape qmake command
+ QStringList &qmk = project->values("QMAKE_QMAKE");
+ qmk = escapeFilePaths(qmk);
+}
+
+bool
+MakefileGenerator::processPrlFile(QString &file)
+{
+ bool ret = false, try_replace_file=false;
+ QString meta_file, orig_file = file;
+ if(QMakeMetaInfo::libExists(file)) {
+ try_replace_file = true;
+ meta_file = file;
+ file = "";
+ } else {
+ QString tmp = file;
+ int ext = tmp.lastIndexOf('.');
+ if(ext != -1)
+ tmp = tmp.left(ext);
+ meta_file = tmp;
+ }
+// meta_file = fileFixify(meta_file);
+ QString real_meta_file = Option::fixPathToLocalOS(meta_file);
+ if(!meta_file.isEmpty()) {
+ QString f = fileFixify(real_meta_file, qmake_getpwd(), Option::output_dir);
+ if(QMakeMetaInfo::libExists(f)) {
+ QMakeMetaInfo libinfo;
+ debug_msg(1, "Processing PRL file: %s", real_meta_file.toLatin1().constData());
+ if(!libinfo.readLib(f)) {
+ fprintf(stderr, "Error processing meta file: %s\n", real_meta_file.toLatin1().constData());
+ } else if(project->isActiveConfig("no_read_prl_" + libinfo.type().toLower())) {
+ debug_msg(2, "Ignored meta file %s [%s]", real_meta_file.toLatin1().constData(), libinfo.type().toLatin1().constData());
+ } else {
+ ret = true;
+ QMap<QString, QStringList> &vars = libinfo.variables();
+ for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it)
+ processPrlVariable(it.key(), it.value());
+ if(try_replace_file && !libinfo.isEmpty("QMAKE_PRL_TARGET")) {
+ QString dir;
+ int slsh = real_meta_file.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ dir = real_meta_file.left(slsh+1);
+ file = libinfo.first("QMAKE_PRL_TARGET");
+ if(QDir::isRelativePath(file))
+ file.prepend(dir);
+ }
+ }
+ }
+ if(ret) {
+ QString mf = QMakeMetaInfo::findLib(meta_file);
+ if(project->values("QMAKE_PRL_INTERNAL_FILES").indexOf(mf) == -1)
+ project->values("QMAKE_PRL_INTERNAL_FILES").append(mf);
+ if(project->values("QMAKE_INTERNAL_INCLUDED_FILES").indexOf(mf) == -1)
+ project->values("QMAKE_INTERNAL_INCLUDED_FILES").append(mf);
+ }
+ }
+ if(try_replace_file && file.isEmpty()) {
+#if 0
+ warn_msg(WarnLogic, "Found prl [%s] file with no target [%s]!", meta_file.toLatin1().constData(),
+ orig_file.toLatin1().constData());
+#endif
+ file = orig_file;
+ }
+ return ret;
+}
+
+void
+MakefileGenerator::filterIncludedFiles(const QString &var)
+{
+ QStringList &inputs = project->values(var);
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ) {
+ if(QMakeSourceFileInfo::included((*input)) > 0)
+ input = inputs.erase(input);
+ else
+ ++input;
+ }
+}
+
+void
+MakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
+{
+ if(var == "QMAKE_PRL_LIBS") {
+ QString where = "QMAKE_LIBS";
+ if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
+ where = project->first("QMAKE_INTERNAL_PRL_LIBS");
+ QStringList &out = project->values(where);
+ for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
+ if(out.indexOf((*it)) == -1)
+ out.append((*it));
+ }
+ } else if(var == "QMAKE_PRL_DEFINES") {
+ QStringList &out = project->values("DEFINES");
+ for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
+ if(out.indexOf((*it)) == -1 &&
+ project->values("PRL_EXPORT_DEFINES").indexOf((*it)) == -1)
+ out.append((*it));
+ }
+ }
+}
+
+void
+MakefileGenerator::processPrlFiles()
+{
+ QHash<QString, bool> processed;
+ for(bool ret = false; true; ret = false) {
+ //read in any prl files included..
+ QStringList l_out;
+ QString where = "QMAKE_LIBS";
+ if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
+ where = project->first("QMAKE_INTERNAL_PRL_LIBS");
+ QStringList &l = project->values(where);
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QString file = (*it);
+ if(!processed.contains(file) && processPrlFile(file)) {
+ processed.insert(file, true);
+ ret = true;
+ }
+ if(!file.isEmpty())
+ l_out.append(file);
+ }
+ if(ret)
+ l = l_out;
+ else
+ break;
+ }
+}
+
+void
+MakefileGenerator::writePrlFile(QTextStream &t)
+{
+ QString target = project->first("TARGET");
+ int slsh = target.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ target.remove(0, slsh + 1);
+ QString bdir = Option::output_dir;
+ if(bdir.isEmpty())
+ bdir = qmake_getpwd();
+ t << "QMAKE_PRL_BUILD_DIR = " << bdir << endl;
+
+ if(!project->projectFile().isEmpty() && project->projectFile() != "-")
+ t << "QMAKE_PRO_INPUT = " << project->projectFile().section('/', -1) << endl;
+
+ if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
+ t << "QMAKE_PRL_SOURCE_DIR = " << project->first("QMAKE_ABSOLUTE_SOURCE_PATH") << endl;
+ t << "QMAKE_PRL_TARGET = " << target << endl;
+ if(!project->isEmpty("PRL_EXPORT_DEFINES"))
+ t << "QMAKE_PRL_DEFINES = " << project->values("PRL_EXPORT_DEFINES").join(" ") << endl;
+ if(!project->isEmpty("PRL_EXPORT_CFLAGS"))
+ t << "QMAKE_PRL_CFLAGS = " << project->values("PRL_EXPORT_CFLAGS").join(" ") << endl;
+ if(!project->isEmpty("PRL_EXPORT_CXXFLAGS"))
+ t << "QMAKE_PRL_CXXFLAGS = " << project->values("PRL_EXPORT_CXXFLAGS").join(" ") << endl;
+ if(!project->isEmpty("CONFIG"))
+ t << "QMAKE_PRL_CONFIG = " << project->values("CONFIG").join(" ") << endl;
+ if(!project->isEmpty("TARGET_VERSION_EXT"))
+ t << "QMAKE_PRL_VERSION = " << project->first("TARGET_VERSION_EXT") << endl;
+ else if(!project->isEmpty("VERSION"))
+ t << "QMAKE_PRL_VERSION = " << project->first("VERSION") << endl;
+ if(project->isActiveConfig("staticlib") || project->isActiveConfig("explicitlib")) {
+ QStringList libs;
+ if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS"))
+ libs = project->values("QMAKE_INTERNAL_PRL_LIBS");
+ else
+ libs << "QMAKE_LIBS"; //obvious one
+ if(project->isActiveConfig("staticlib"))
+ libs << "QMAKE_LIBS_PRIVATE";
+ t << "QMAKE_PRL_LIBS = ";
+ for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it)
+ t << project->values((*it)).join(" ").replace('\\', "\\\\") << " ";
+ t << endl;
+ }
+}
+
+bool
+MakefileGenerator::writeProjectMakefile()
+{
+ usePlatformDir();
+ QTextStream t(&Option::output);
+
+ //header
+ writeHeader(t);
+
+ QList<SubTarget*> targets;
+ {
+ QStringList builds = project->values("BUILDS");
+ for(QStringList::Iterator it = builds.begin(); it != builds.end(); ++it) {
+ SubTarget *st = new SubTarget;
+ targets.append(st);
+ st->makefile = "$(MAKEFILE)." + (*it);
+ st->name = (*it);
+ st->target = project->isEmpty((*it) + ".target") ? (*it) : project->first((*it) + ".target");
+ }
+ }
+ if(project->isActiveConfig("build_all")) {
+ t << "first: all" << endl;
+ QList<SubTarget*>::Iterator it;
+
+ //install
+ t << "install: ";
+ for(it = targets.begin(); it != targets.end(); ++it)
+ t << (*it)->target << "-install ";
+ t << endl;
+
+ //uninstall
+ t << "uninstall: ";
+ for(it = targets.begin(); it != targets.end(); ++it)
+ t << (*it)->target << "-uninstall ";
+ t << endl;
+ } else {
+ t << "first: " << targets.first()->target << endl
+ << "install: " << targets.first()->target << "-install" << endl
+ << "uninstall: " << targets.first()->target << "-uninstall" << endl;
+ }
+
+ writeSubTargets(t, targets, SubTargetsNoFlags);
+ if(!project->isActiveConfig("no_autoqmake")) {
+ for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it)
+ t << (*it)->makefile << ": " <<
+ Option::fixPathToTargetOS(fileFixify(Option::output.fileName())) << endl;
+ }
+ qDeleteAll(targets);
+ return true;
+}
+
+bool
+MakefileGenerator::write()
+{
+ if(!project)
+ return false;
+ writePrlFile();
+ if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || //write makefile
+ Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
+ QTextStream t(&Option::output);
+ if(!writeMakefile(t)) {
+#if 1
+ warn_msg(WarnLogic, "Unable to generate output for: %s [TEMPLATE %s]",
+ Option::output.fileName().toLatin1().constData(),
+ project->first("TEMPLATE").toLatin1().constData());
+ if(Option::output.exists())
+ Option::output.remove();
+#endif
+ }
+ }
+ return true;
+}
+
+QString
+MakefileGenerator::prlFileName(bool fixify)
+{
+ QString ret = project->first("TARGET_PRL");;
+ if(ret.isEmpty())
+ ret = project->first("TARGET");
+ int slsh = ret.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ ret.remove(0, slsh);
+ if(!ret.endsWith(Option::prl_ext)) {
+ int dot = ret.indexOf('.');
+ if(dot != -1)
+ ret.truncate(dot);
+ ret += Option::prl_ext;
+ }
+ if(!project->isEmpty("QMAKE_BUNDLE"))
+ ret.prepend(project->first("QMAKE_BUNDLE") + Option::dir_sep);
+ if(fixify) {
+ if(!project->isEmpty("DESTDIR"))
+ ret.prepend(project->first("DESTDIR"));
+ ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir));
+ }
+ return ret;
+}
+
+void
+MakefileGenerator::writePrlFile()
+{
+ if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
+ Option::qmake_mode == Option::QMAKE_GENERATE_PRL)
+ && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()
+ && project->isActiveConfig("create_prl")
+ && (project->first("TEMPLATE") == "lib"
+ || project->first("TEMPLATE") == "vclib")
+ && !project->isActiveConfig("plugin")) { //write prl file
+ QString local_prl = prlFileName();
+ QString prl = fileFixify(local_prl);
+ mkdir(fileInfo(local_prl).path());
+ QFile ft(local_prl);
+ if(ft.open(QIODevice::WriteOnly)) {
+ project->values("ALL_DEPS").append(prl);
+ project->values("QMAKE_INTERNAL_PRL_FILE").append(prl);
+ QTextStream t(&ft);
+ writePrlFile(t);
+ }
+ }
+}
+
+// Manipulate directories, so it's possible to build
+// several cross-platform targets concurrently
+void
+MakefileGenerator::usePlatformDir()
+{
+ QString pltDir(project->first("QMAKE_PLATFORM_DIR"));
+ if(pltDir.isEmpty())
+ return;
+ QChar sep = QDir::separator();
+ QString slashPltDir = sep + pltDir;
+
+ QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"),
+ QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"),
+ QString("PRECOMPILED_DIR"), QString("QMAKE_LIBDIR_QT"), QString() };
+ for(int i = 0; !dirs[i].isEmpty(); ++i) {
+ QString filePath = project->first(dirs[i]);
+ project->values(dirs[i]) = QStringList(filePath + (filePath.isEmpty() ? pltDir : slashPltDir));
+ }
+
+ QString libs[] = { QString("QMAKE_LIBS_QT"), QString("QMAKE_LIBS_QT_THREAD"), QString("QMAKE_LIBS_QT_ENTRY"), QString() };
+ for(int i = 0; !libs[i].isEmpty(); ++i) {
+ QString filePath = project->first(libs[i]);
+ int fpi = filePath.lastIndexOf(sep);
+ if(fpi == -1)
+ project->values(libs[i]).prepend(pltDir + sep);
+ else
+ project->values(libs[i]) = QStringList(filePath.left(fpi) + slashPltDir + filePath.mid(fpi));
+ }
+}
+
+void
+MakefileGenerator::writeObj(QTextStream &t, const QString &src)
+{
+ QStringList &srcl = project->values(src);
+ QStringList objl = createObjectList(srcl);
+
+ QStringList::Iterator oit = objl.begin();
+ QStringList::Iterator sit = srcl.begin();
+ QString stringSrc("$src");
+ QString stringObj("$obj");
+ for(;sit != srcl.end() && oit != objl.end(); ++oit, ++sit) {
+ if((*sit).isEmpty())
+ continue;
+
+ t << escapeDependencyPath((*oit)) << ": " << escapeDependencyPath((*sit)) << " " << escapeDependencyPaths(findDependencies((*sit))).join(" \\\n\t\t");
+
+ QString comp, cimp;
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) {
+ if((*sit).endsWith((*cppit))) {
+ comp = "QMAKE_RUN_CXX";
+ cimp = "QMAKE_RUN_CXX_IMP";
+ break;
+ }
+ }
+ if(comp.isEmpty()) {
+ comp = "QMAKE_RUN_CC";
+ cimp = "QMAKE_RUN_CC_IMP";
+ }
+ bool use_implicit_rule = !project->isEmpty(cimp);
+ use_implicit_rule = false;
+ if(use_implicit_rule) {
+ if(!project->isEmpty("OBJECTS_DIR")) {
+ use_implicit_rule = false;
+ } else {
+ int dot = (*sit).lastIndexOf('.');
+ if(dot == -1 || ((*sit).left(dot) + Option::obj_ext != (*oit)))
+ use_implicit_rule = false;
+ }
+ }
+ if (!use_implicit_rule && !project->isEmpty(comp)) {
+ QString p = var(comp), srcf(*sit);
+ p.replace(stringSrc, escapeFilePath(srcf));
+ p.replace(stringObj, escapeFilePath((*oit)));
+ t << "\n\t" << p;
+ }
+ t << endl << endl;
+ }
+}
+
+QString
+MakefileGenerator::filePrefixRoot(const QString &root, const QString &path)
+{
+ QString ret(root + path);
+ if(path.length() > 2 && path[1] == ':') //c:\foo
+ ret = QString(path.mid(0, 2) + root + path.mid(2));
+ while(ret.endsWith("\\"))
+ ret = ret.left(ret.length()-1);
+ return ret;
+}
+
+void
+MakefileGenerator::writeInstalls(QTextStream &t, const QString &installs, bool noBuild)
+{
+ QString rm_dir_contents("-$(DEL_FILE)");
+ if (!isWindowsShell()) //ick
+ rm_dir_contents = "-$(DEL_FILE) -r";
+
+ QString all_installs, all_uninstalls;
+ QStringList &l = project->values(installs);
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ QString pvar = (*it) + ".path";
+ if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 &&
+ project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1 &&
+ project->values(pvar).isEmpty()) {
+ warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.toLatin1().constData());
+ continue;
+ }
+
+ bool do_default = true;
+ const QString root = "$(INSTALL_ROOT)";
+ QString target, dst;
+ if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 &&
+ project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1) {
+ dst = fileFixify(unescapeFilePath(project->values(pvar).first()), FileFixifyAbsolute, false);
+ if(!dst.endsWith(Option::dir_sep))
+ dst += Option::dir_sep;
+ }
+ dst = escapeFilePath(dst);
+
+ QStringList tmp, uninst = project->values((*it) + ".uninstall");
+ //other
+ tmp = project->values((*it) + ".extra");
+ if(tmp.isEmpty())
+ tmp = project->values((*it) + ".commands"); //to allow compatible name
+ if(!tmp.isEmpty()) {
+ do_default = false;
+ if(!target.isEmpty())
+ target += "\n\t";
+ target += tmp.join(" ");
+ }
+ //masks
+ tmp = findFilesInVPATH(project->values((*it) + ".files"), VPATH_NoFixify);
+ tmp = fileFixify(tmp, FileFixifyAbsolute);
+ if(!tmp.isEmpty()) {
+ if(!target.isEmpty())
+ target += "\n";
+ do_default = false;
+ for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) {
+ QString wild = Option::fixPathToTargetOS((*wild_it), false, false);
+ QString dirstr = qmake_getpwd(), filestr = wild;
+ int slsh = filestr.lastIndexOf(Option::dir_sep);
+ if(slsh != -1) {
+ dirstr = filestr.left(slsh+1);
+ filestr.remove(0, slsh+1);
+ }
+ if(!dirstr.endsWith(Option::dir_sep))
+ dirstr += Option::dir_sep;
+ bool is_target = (wild == fileFixify(var("TARGET"), FileFixifyAbsolute));
+ if(is_target || exists(wild)) { //real file or target
+ QString file = wild;
+ QFileInfo fi(fileInfo(wild));
+ if(!target.isEmpty())
+ target += "\t";
+ QString dst_file = filePrefixRoot(root, dst);
+ if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
+ if(!dst_file.endsWith(Option::dir_sep))
+ dst_file += Option::dir_sep;
+ dst_file += fi.fileName();
+ }
+ QString cmd;
+ if (fi.isDir())
+ cmd = "-$(INSTALL_DIR)";
+ else if (is_target || fi.isExecutable())
+ cmd = "-$(INSTALL_PROGRAM)";
+ else
+ cmd = "-$(INSTALL_FILE)";
+ cmd += " " + escapeFilePath(wild) + " " + dst_file + "\n";
+ target += cmd;
+ if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
+ !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
+ target += QString("\t-") + var("QMAKE_STRIP") + " " +
+ filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + "\n";
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)));
+ continue;
+ }
+ QString local_dirstr = Option::fixPathToLocalOS(dirstr, true);
+ QStringList files = QDir(local_dirstr).entryList(QStringList(filestr));
+ const QStringList &installConfigValues = project->values((*it) + ".CONFIG");
+ if (installConfigValues.contains("no_check_exist") && files.isEmpty()) {
+ if(!target.isEmpty())
+ target += "\t";
+ QString dst_file = filePrefixRoot(root, dst);
+ QFileInfo fi(fileInfo(wild));
+ QString cmd;
+ if (installConfigValues.contains("directory")) {
+ cmd = QLatin1String("-$(INSTALL_DIR)");
+ if (!dst_file.endsWith(Option::dir_sep))
+ dst_file += Option::dir_sep;
+ dst_file += fi.fileName();
+ } else if (installConfigValues.contains("executable")) {
+ cmd = QLatin1String("-$(INSTALL_PROGRAM)");
+ } else if (installConfigValues.contains("data")) {
+ cmd = QLatin1String("-$(INSTALL_FILE)");
+ } else {
+ cmd = QString(fi.isExecutable() ? "-$(INSTALL_PROGRAM)" : "-$(INSTALL_FILE)");
+ }
+ cmd += " " + wild + " " + dst_file + "\n";
+ target += cmd;
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)));
+ }
+ for(int x = 0; x < files.count(); x++) {
+ QString file = files[x];
+ if(file == "." || file == "..") //blah
+ continue;
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)));
+ QFileInfo fi(fileInfo(dirstr + file));
+ if(!target.isEmpty())
+ target += "\t";
+ QString dst_file = filePrefixRoot(root, fileFixify(dst, FileFixifyAbsolute, false));
+ if(fi.isDir() && project->isActiveConfig("copy_dir_files")) {
+ if(!dst_file.endsWith(Option::dir_sep))
+ dst_file += Option::dir_sep;
+ dst_file += fi.fileName();
+ }
+ QString cmd = QString(fi.isDir() ? "-$(INSTALL_DIR)" : "-$(INSTALL_FILE)") + " " +
+ dirstr + file + " " + dst_file + "\n";
+ target += cmd;
+ if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") &&
+ !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
+ target += QString("\t-") + var("QMAKE_STRIP") + " " +
+ filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) +
+ "\n";
+ }
+ }
+ }
+ //default?
+ if(do_default) {
+ target = defaultInstall((*it));
+ uninst = project->values((*it) + ".uninstall");
+ }
+
+ if(!target.isEmpty() || project->values((*it) + ".CONFIG").indexOf("dummy_install") != -1) {
+ if(noBuild || project->values((*it) + ".CONFIG").indexOf("no_build") != -1)
+ t << "install_" << (*it) << ":";
+ else if(project->isActiveConfig("build_all"))
+ t << "install_" << (*it) << ": all";
+ else
+ t << "install_" << (*it) << ": first";
+ const QStringList &deps = project->values((*it) + ".depends");
+ if(!deps.isEmpty()) {
+ for(QStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) {
+ QString targ = var((*dep_it) + ".target");
+ if(targ.isEmpty())
+ targ = (*dep_it);
+ t << " " << escapeDependencyPath(targ);
+ }
+ }
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\t";
+ const QStringList &dirs = project->values(pvar);
+ for(QStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) {
+ QString tmp_dst = fileFixify((*pit), FileFixifyAbsolute, false);
+ if (!isWindowsShell() && !tmp_dst.endsWith(Option::dir_sep))
+ tmp_dst += Option::dir_sep;
+ t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t";
+ }
+ t << target << endl << endl;
+ if(!uninst.isEmpty()) {
+ t << "uninstall_" << (*it) << ": ";
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\t"
+ << uninst.join(" ") << "\n\t"
+ << "-$(DEL_DIR) " << filePrefixRoot(root, dst) << " " << endl << endl;
+ }
+ t << endl;
+
+ if(project->values((*it) + ".CONFIG").indexOf("no_default_install") == -1) {
+ all_installs += QString("install_") + (*it) + " ";
+ if(!uninst.isEmpty())
+ all_uninstalls += "uninstall_" + (*it) + " ";
+ }
+ } else {
+ debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData());
+ }
+ }
+ t << "install: " << var("INSTALLDEPS") << " " << all_installs;
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\n";
+ t << "uninstall: " << all_uninstalls << " " << var("UNINSTALLDEPS");
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\n";
+}
+
+QString
+MakefileGenerator::var(const QString &var)
+{
+ return val(project->values(var));
+}
+
+QString
+MakefileGenerator::val(const QStringList &varList)
+{
+ return valGlue(varList, "", " ", "");
+}
+
+QString
+MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after)
+{
+ return valGlue(project->values(var), before, glue, after);
+}
+
+QString
+MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after)
+{
+ QString ret;
+ for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) {
+ if(!(*it).isEmpty()) {
+ if(!ret.isEmpty())
+ ret += glue;
+ ret += (*it);
+ }
+ }
+ return ret.isEmpty() ? QString("") : before + ret + after;
+}
+
+
+QString
+MakefileGenerator::varList(const QString &var)
+{
+ return valList(project->values(var));
+}
+
+QString
+MakefileGenerator::valList(const QStringList &varList)
+{
+ return valGlue(varList, "", " \\\n\t\t", "");
+}
+
+QStringList
+MakefileGenerator::createObjectList(const QStringList &sources)
+{
+ QStringList ret;
+ QString objdir;
+ if(!project->values("OBJECTS_DIR").isEmpty())
+ objdir = project->first("OBJECTS_DIR");
+ for(QStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
+ QFileInfo fi(fileInfo(Option::fixPathToLocalOS((*it))));
+ QString dir;
+ if(objdir.isEmpty() && project->isActiveConfig("object_with_source")) {
+ QString fName = Option::fixPathToTargetOS((*it), false);
+ int dl = fName.lastIndexOf(Option::dir_sep);
+ if(dl != -1)
+ dir = fName.left(dl + 1);
+ } else {
+ dir = objdir;
+ }
+ ret.append(dir + fi.completeBaseName() + Option::obj_ext);
+ }
+ return ret;
+}
+
+ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o)
+{
+ static QString doubleColon = QLatin1String("::");
+
+ hash = 0;
+ pwd = qmake_getpwd();
+ var = v;
+ {
+ QStringList il = i;
+ il.sort();
+ in = il.join(doubleColon);
+ }
+ {
+ QStringList ol = o;
+ ol.sort();
+ out = ol.join(doubleColon);
+ }
+}
+
+bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const
+{
+ return (hashCode() == f.hashCode() &&
+ f.in == in &&
+ f.out == out &&
+ f.var == var &&
+ f.pwd == pwd);
+}
+
+
+QString
+MakefileGenerator::replaceExtraCompilerVariables(const QString &orig_var, const QStringList &in, const QStringList &out)
+{
+ //lazy cache
+ ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out);
+ QString cacheVal = extraCompilerVariablesCache.value(cacheKey);
+ if(!cacheVal.isNull())
+ return cacheVal;
+
+ //do the work
+ QString ret = orig_var;
+ QRegExp reg_var("\\$\\{.*\\}");
+ reg_var.setMinimal(true);
+ for(int rep = 0; (rep = reg_var.indexIn(ret, rep)) != -1; ) {
+ QStringList val;
+ const QString var = ret.mid(rep + 2, reg_var.matchedLength() - 3);
+ bool filePath = false;
+ if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) {
+ const QString varname = var.mid(10);
+ val += project->values(varname);
+ }
+ if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) {
+ const QString varname = var.mid(16);
+ val += project->first(varname);
+ }
+
+ if(val.isEmpty() && !in.isEmpty()) {
+ if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) {
+ filePath = true;
+ const QString funcname = var.mid(19);
+ val += project->expand(funcname, QList<QStringList>() << in);
+ } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) {
+ //filePath = true;
+ for(int i = 0; i < in.size(); ++i) {
+ QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
+ QString base = fi.completeBaseName();
+ if(base.isNull())
+ base = fi.fileName();
+ val += base;
+ }
+ } else if(var == QLatin1String("QMAKE_FILE_EXT")) {
+ filePath = true;
+ for(int i = 0; i < in.size(); ++i) {
+ QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i))));
+ QString ext;
+ // Ensure complementarity with QMAKE_FILE_BASE
+ int baseLen = fi.completeBaseName().length();
+ if(baseLen == 0)
+ ext = fi.fileName();
+ else
+ ext = fi.fileName().remove(0, baseLen);
+ val += ext;
+ }
+ } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) {
+ filePath = true;
+ for(int i = 0; i < in.size(); ++i)
+ val += fileInfo(Option::fixPathToLocalOS(in.at(i))).path();
+ } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) {
+ filePath = true;
+ for(int i = 0; i < in.size(); ++i)
+ val += fileInfo(Option::fixPathToLocalOS(in.at(i))).filePath();
+
+ }
+ }
+ if(val.isEmpty() && !out.isEmpty()) {
+ if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) {
+ filePath = true;
+ const QString funcname = var.mid(20);
+ val += project->expand(funcname, QList<QStringList>() << out);
+ } else if(var == QLatin1String("QMAKE_FILE_OUT")) {
+ filePath = true;
+ for(int i = 0; i < out.size(); ++i)
+ val += fileInfo(Option::fixPathToLocalOS(out.at(i))).filePath();
+ } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) {
+ //filePath = true;
+ for(int i = 0; i < out.size(); ++i) {
+ QFileInfo fi(fileInfo(Option::fixPathToLocalOS(out.at(i))));
+ QString base = fi.completeBaseName();
+ if(base.isNull())
+ base = fi.fileName();
+ val += base;
+ }
+ }
+ }
+ if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) {
+ const QString funcname = var.mid(11);
+ val += project->expand(funcname, QList<QStringList>() << in << out);
+ }
+
+ if(!val.isEmpty()) {
+ QString fullVal;
+ if(filePath) {
+ for(int i = 0; i < val.size(); ++i) {
+ const QString file = Option::fixPathToTargetOS(unescapeFilePath(val.at(i)), false);
+ if(!fullVal.isEmpty())
+ fullVal += " ";
+ fullVal += escapeFilePath(file);
+ }
+ } else {
+ fullVal = val.join(" ");
+ }
+ ret.replace(rep, reg_var.matchedLength(), fullVal);
+ rep += fullVal.length();
+ } else {
+ rep += reg_var.matchedLength();
+ }
+ }
+
+ //cache the value
+ extraCompilerVariablesCache.insert(cacheKey, ret);
+ return ret;
+}
+
+bool
+MakefileGenerator::verifyExtraCompiler(const QString &comp, const QString &file_unfixed)
+{
+ if(noIO())
+ return false;
+ const QString file = Option::fixPathToLocalOS(file_unfixed);
+
+ if(project->values(comp + ".CONFIG").indexOf("moc_verify") != -1) {
+ if(!file.isNull()) {
+ QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS);
+ if(!mocable(file)) {
+ return false;
+ } else {
+ project->values("MOCABLES").append(file);
+ }
+ }
+ } else if(project->values(comp + ".CONFIG").indexOf("function_verify") != -1) {
+ QString tmp_out = project->values(comp + ".output").first();
+ if(tmp_out.isEmpty())
+ return false;
+ QStringList verify_function = project->values(comp + ".verify_function");
+ if(verify_function.isEmpty())
+ return false;
+
+ for(int i = 0; i < verify_function.size(); ++i) {
+ bool invert = false;
+ QString verify = verify_function.at(i);
+ if(verify.at(0) == QLatin1Char('!')) {
+ invert = true;
+ verify = verify.mid(1);
+ }
+
+ if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
+ bool pass = project->test(verify, QList<QStringList>() << QStringList(tmp_out) << QStringList(file));
+ if(invert)
+ pass = !pass;
+ if(!pass)
+ return false;
+ } else {
+ QStringList &tmp = project->values(comp + ".input");
+ for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ QStringList &inputs = project->values((*it));
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
+ if((*input).isEmpty())
+ continue;
+ QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
+ if(in == file) {
+ bool pass = project->test(verify,
+ QList<QStringList>() << QStringList(replaceExtraCompilerVariables(tmp_out, (*input), QString())) <<
+ QStringList(file));
+ if(invert)
+ pass = !pass;
+ if(!pass)
+ return false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else if(project->values(comp + ".CONFIG").indexOf("verify") != -1) {
+ QString tmp_out = project->values(comp + ".output").first();
+ if(tmp_out.isEmpty())
+ return false;
+ QString tmp_cmd;
+ if(!project->isEmpty(comp + ".commands")) {
+ int argv0 = -1;
+ QStringList cmdline = project->values(comp + ".commands");
+ for(int i = 0; i < cmdline.count(); ++i) {
+ if(!cmdline.at(i).contains('=')) {
+ argv0 = i;
+ break;
+ }
+ }
+ if(argv0 != -1) {
+ cmdline[argv0] = Option::fixPathToTargetOS(cmdline.at(argv0), false);
+ tmp_cmd = cmdline.join(" ");
+ }
+ }
+
+ if(project->values(comp + ".CONFIG").indexOf("combine") != -1) {
+ QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out);
+ if(system(cmd.toLatin1().constData()))
+ return false;
+ } else {
+ QStringList &tmp = project->values(comp + ".input");
+ for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ QStringList &inputs = project->values((*it));
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
+ if((*input).isEmpty())
+ continue;
+ QString in = fileFixify(Option::fixPathToTargetOS((*input), false));
+ if(in == file) {
+ QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
+ QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out);
+ if(system(cmd.toLatin1().constData()))
+ return false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void
+MakefileGenerator::writeExtraTargets(QTextStream &t)
+{
+ QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
+ for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
+ QString targ = var((*it) + ".target"),
+ cmd = var((*it) + ".commands"), deps;
+ if(targ.isEmpty())
+ targ = (*it);
+ QStringList &deplist = project->values((*it) + ".depends");
+ for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
+ QString dep = var((*dep_it) + ".target");
+ if(dep.isEmpty())
+ dep = (*dep_it);
+ deps += " " + escapeDependencyPath(dep);
+ }
+ if(project->values((*it) + ".CONFIG").indexOf("fix_target") != -1)
+ targ = fileFixify(targ, Option::output_dir, Option::output_dir);
+ if(project->isEmpty("QMAKE_NOFORCE") &&
+ project->values((*it) + ".CONFIG").indexOf("phony") != -1)
+ deps += QString(" ") + "FORCE";
+ t << escapeDependencyPath(targ) << ":" << deps;
+ if(!cmd.isEmpty())
+ t << "\n\t" << cmd;
+ t << endl << endl;
+
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ);
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts);
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd;
+ }
+}
+
+void
+MakefileGenerator::writeExtraCompilerTargets(QTextStream &t)
+{
+ QString clean_targets;
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = fileFixify(project->values((*it) + ".output").first(),
+ Option::output_dir, Option::output_dir);
+ QString tmp_cmd;
+ if(!project->isEmpty((*it) + ".commands")) {
+ QStringList cmdline = project->values((*it) + ".commands");
+ int argv0 = findExecutable(cmdline);
+ if(argv0 != -1) {
+ cmdline[argv0] = escapeFilePath(Option::fixPathToTargetOS(cmdline.at(argv0), false));
+ tmp_cmd = cmdline.join(" ");
+ }
+ }
+ QStringList tmp_dep = project->values((*it) + ".depends");
+ QString tmp_dep_cmd;
+ QString dep_cd_cmd;
+ if(!project->isEmpty((*it) + ".depend_command")) {
+ int argv0 = -1;
+ QStringList cmdline = project->values((*it) + ".depend_command");
+ for(int i = 0; i < cmdline.count(); ++i) {
+ if(!cmdline.at(i).contains('=')) {
+ argv0 = i;
+ break;
+ }
+ }
+ if(argv0 != -1) {
+ const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true);
+ if(exists(c)) {
+ cmdline[argv0] = escapeFilePath(Option::fixPathToLocalOS(cmdline.at(argv0), false));
+ } else {
+ cmdline[argv0] = escapeFilePath(cmdline.at(argv0));
+ }
+ QFileInfo cmdFileInfo(cmdline[argv0]);
+ if (!cmdFileInfo.isAbsolute() || cmdFileInfo.exists())
+ tmp_dep_cmd = cmdline.join(" ");
+ }
+ dep_cd_cmd = QLatin1String("cd ")
+ + escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false))
+ + QLatin1String(" && ");
+ }
+ QStringList &vars = project->values((*it) + ".variables");
+ if(tmp_out.isEmpty() || tmp_cmd.isEmpty())
+ continue;
+ QStringList tmp_inputs;
+ {
+ const QStringList &comp_inputs = project->values((*it) + ".input");
+ for(QStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) {
+ const QStringList &tmp = project->values((*it2));
+ for(QStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) {
+ QString in = Option::fixPathToTargetOS((*input), false);
+ if(verifyExtraCompiler((*it), in))
+ tmp_inputs.append((*input));
+ }
+ }
+ }
+
+ t << "compiler_" << (*it) << "_make_all:";
+ if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
+ // compilers with a combined input only have one output
+ QString input = project->values((*it) + ".output").first();
+ t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, input, QString()));
+ } else {
+ for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
+ QString in = Option::fixPathToTargetOS((*input), false);
+ t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, (*input), QString()));
+ }
+ }
+ t << endl;
+
+ if(project->values((*it) + ".CONFIG").indexOf("no_clean") == -1) {
+ QString tmp_clean = project->values((*it) + ".clean").join(" ");
+ QString tmp_clean_cmds = project->values((*it) + ".clean_commands").join(" ");
+ if(!tmp_inputs.isEmpty())
+ clean_targets += QString("compiler_" + (*it) + "_clean ");
+ t << "compiler_" << (*it) << "_clean:";
+ bool wrote_clean_cmds = false, wrote_clean = false;
+ if(tmp_clean_cmds.isEmpty()) {
+ wrote_clean_cmds = true;
+ } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) {
+ t << "\n\t" << tmp_clean_cmds;
+ wrote_clean_cmds = true;
+ }
+ if(tmp_clean.isEmpty())
+ tmp_clean = tmp_out;
+ if(tmp_clean.indexOf("${QMAKE_") == -1) {
+ t << "\n\t" << "-$(DEL_FILE) " << tmp_clean;
+ wrote_clean = true;
+ }
+ if(!wrote_clean_cmds || !wrote_clean) {
+ QStringList cleans;
+ const QString del_statement("-$(DEL_FILE)");
+ if(!wrote_clean) {
+ if(project->isActiveConfig("no_delete_multiple_files")) {
+ for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input)
+ cleans.append(" " + replaceExtraCompilerVariables(tmp_clean, (*input),
+ replaceExtraCompilerVariables(tmp_out, (*input), QString())));
+ } else {
+ QString files, file;
+ const int commandlineLimit = 2047; // NT limit, expanded
+ for(int input = 0; input < tmp_inputs.size(); ++input) {
+ file = " " + replaceExtraCompilerVariables(tmp_clean, tmp_inputs.at(input),
+ replaceExtraCompilerVariables(tmp_out, tmp_inputs.at(input), QString()));
+ if(del_statement.length() + files.length() +
+ qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) {
+ cleans.append(files);
+ files.clear();
+ }
+ files += file;
+ }
+ if(!files.isEmpty())
+ cleans.append(files);
+ }
+ }
+ if(!cleans.isEmpty())
+ t << valGlue(cleans, "\n\t" + del_statement, "\n\t" + del_statement, "");
+ if(!wrote_clean_cmds) {
+ for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
+ t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, (*input),
+ replaceExtraCompilerVariables(tmp_out, (*input), QString()));
+ }
+ }
+ }
+ t << endl;
+ }
+ if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) {
+ if(tmp_out.indexOf("${QMAKE_") != -1) {
+ warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.",
+ (*it).toLatin1().constData());
+ continue;
+ }
+ QStringList deps, inputs;
+ if(!tmp_dep.isEmpty())
+ deps += fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
+ for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
+ deps += findDependencies((*input));
+ inputs += Option::fixPathToTargetOS((*input), false);
+ if(!tmp_dep_cmd.isEmpty() && doDepends()) {
+ char buff[256];
+ QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input),
+ tmp_out);
+ dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
+ if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
+ QString indeps;
+ while(!feof(proc)) {
+ int read_in = (int)fread(buff, 1, 255, proc);
+ if(!read_in)
+ break;
+ indeps += QByteArray(buff, read_in);
+ }
+ QT_PCLOSE(proc);
+ if(!indeps.isEmpty()) {
+ QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
+ for(int i = 0; i < dep_cmd_deps.count(); ++i) {
+ QString &file = dep_cmd_deps[i];
+ if(!exists(file)) {
+ QString localFile;
+ QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
+ for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
+ it != depdirs.end(); ++it) {
+ if(exists((*it).real() + Option::dir_sep + file)) {
+ localFile = (*it).local() + Option::dir_sep + file;
+ break;
+ }
+ }
+ file = localFile;
+ }
+ if(!file.isEmpty())
+ file = fileFixify(file);
+ }
+ deps += dep_cmd_deps;
+ }
+ }
+ }
+ }
+ for(int i = 0; i < inputs.size(); ) {
+ if(tmp_out == inputs.at(i))
+ inputs.removeAt(i);
+ else
+ ++i;
+ }
+ for(int i = 0; i < deps.size(); ) {
+ if(tmp_out == deps.at(i))
+ deps.removeAt(i);
+ else
+ ++i;
+ }
+ if (inputs.isEmpty())
+ continue;
+
+ QString cmd;
+ if (isForSymbianSbsv2()) {
+ // In sbsv2 the command inputs and outputs need to use absolute paths
+ cmd = replaceExtraCompilerVariables(tmp_cmd,
+ fileFixify(escapeFilePaths(inputs), FileFixifyAbsolute),
+ fileFixify(QStringList(tmp_out), FileFixifyAbsolute));
+ } else {
+ cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out));
+ }
+
+ t << escapeDependencyPath(tmp_out) << ":";
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out);
+ // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies
+ if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) {
+ t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir)));
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep;
+ } else {
+ t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps));
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps;
+ }
+ t << "\n\t" << cmd << endl << endl;
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd;
+ continue;
+ }
+ for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) {
+ QString in = Option::fixPathToTargetOS((*input), false);
+ QStringList deps = findDependencies((*input));
+ deps += escapeDependencyPath(in);
+ QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString());
+ if(!tmp_dep.isEmpty()) {
+ QStringList pre_deps = fileFixify(tmp_dep, Option::output_dir, Option::output_dir);
+ for(int i = 0; i < pre_deps.size(); ++i)
+ deps += replaceExtraCompilerVariables(pre_deps.at(i), (*input), out);
+ }
+ QString cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
+ // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use!
+ if (isForSymbianSbsv2()) {
+ // In sbsv2 the command inputs and outputs need to use absolute paths
+ cmd = replaceExtraCompilerVariables(tmp_cmd,
+ fileFixify((*input), FileFixifyAbsolute),
+ fileFixify(out, FileFixifyAbsolute));
+ } else {
+ cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out);
+ }
+ for(QStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3)
+ cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")");
+ if(!tmp_dep_cmd.isEmpty() && doDepends()) {
+ char buff[256];
+ QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), out);
+ dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd);
+ if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) {
+ QString indeps;
+ while(!feof(proc)) {
+ int read_in = (int)fread(buff, 1, 255, proc);
+ if(!read_in)
+ break;
+ indeps += QByteArray(buff, read_in);
+ }
+ QT_PCLOSE(proc);
+ if(!indeps.isEmpty()) {
+ QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' ');
+ for(int i = 0; i < dep_cmd_deps.count(); ++i) {
+ QString &file = dep_cmd_deps[i];
+ if(!exists(file)) {
+ QString localFile;
+ QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
+ for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin();
+ it != depdirs.end(); ++it) {
+ if(exists((*it).real() + Option::dir_sep + file)) {
+ localFile = (*it).local() + Option::dir_sep + file;
+ break;
+ }
+ }
+ file = localFile;
+ }
+ if(!file.isEmpty())
+ file = fileFixify(file);
+ }
+ deps += dep_cmd_deps;
+ }
+ }
+ //use the depend system to find includes of these included files
+ QStringList inc_deps;
+ for(int i = 0; i < deps.size(); ++i) {
+ const QString dep = deps.at(i);
+ if(QFile::exists(dep)) {
+ SourceFileType type = TYPE_UNKNOWN;
+ if(type == TYPE_UNKNOWN) {
+ for(QStringList::Iterator cit = Option::c_ext.begin();
+ cit != Option::c_ext.end(); ++cit) {
+ if(dep.endsWith((*cit))) {
+ type = TYPE_C;
+ break;
+ }
+ }
+ }
+ if(type == TYPE_UNKNOWN) {
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin();
+ cppit != Option::cpp_ext.end(); ++cppit) {
+ if(dep.endsWith((*cppit))) {
+ type = TYPE_C;
+ break;
+ }
+ }
+ }
+ if(type == TYPE_UNKNOWN) {
+ for(QStringList::Iterator hit = Option::h_ext.begin();
+ type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) {
+ if(dep.endsWith((*hit))) {
+ type = TYPE_C;
+ break;
+ }
+ }
+ }
+ if(type != TYPE_UNKNOWN) {
+ if(!QMakeSourceFileInfo::containsSourceFile(dep, type))
+ QMakeSourceFileInfo::addSourceFile(dep, type);
+ inc_deps += QMakeSourceFileInfo::dependencies(dep);
+ }
+ }
+ }
+ deps += inc_deps;
+ }
+ for(int i = 0; i < deps.size(); ) {
+ QString &dep = deps[i];
+ dep = Option::fixPathToTargetOS(unescapeFilePath(dep), false);
+ if(out == dep)
+ deps.removeAt(i);
+ else
+ ++i;
+ }
+ t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t"
+ << cmd << endl << endl;
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out);
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps;
+ project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd;
+ }
+ }
+ t << "compiler_clean: " << clean_targets << endl << endl;
+}
+
+void
+MakefileGenerator::writeExtraCompilerVariables(QTextStream &t)
+{
+ bool first = true;
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ const QStringList &vars = project->values((*it) + ".variables");
+ for(QStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) {
+ if(first) {
+ t << "\n####### Custom Compiler Variables" << endl;
+ first = false;
+ }
+ t << "QMAKE_COMP_" << (*varit) << " = "
+ << valList(project->values((*varit))) << endl;
+ }
+ }
+ if(!first)
+ t << endl;
+}
+
+void
+MakefileGenerator::writeExtraVariables(QTextStream &t)
+{
+ bool first = true;
+ QMap<QString, QStringList> &vars = project->variables();
+ QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES");
+ for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
+ for(QStringList::Iterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) {
+ QRegExp rx((*exp_it), Qt::CaseInsensitive, QRegExp::Wildcard);
+ if(rx.exactMatch(it.key())) {
+ if(first) {
+ t << "\n####### Custom Variables" << endl;
+ first = false;
+ }
+ t << "EXPORT_" << it.key() << " = " << it.value().join(" ") << endl;
+ }
+ }
+ }
+ if(!first)
+ t << endl;
+}
+
+bool
+MakefileGenerator::writeStubMakefile(QTextStream &t)
+{
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
+ for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
+ t << *it << " ";
+ //const QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
+ t << "first all clean install distclean uninstall: " << "qmake" << endl
+ << "qmake_all:" << endl;
+ writeMakeQmake(t);
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << "FORCE:" << endl << endl;
+ return true;
+}
+
+bool
+MakefileGenerator::writeMakefile(QTextStream &t)
+{
+ t << "####### Compile" << endl << endl;
+ writeObj(t, "SOURCES");
+ writeObj(t, "GENERATED_SOURCES");
+
+ t << "####### Install" << endl << endl;
+ writeInstalls(t, "INSTALLS");
+
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << "FORCE:" << endl << endl;
+ return true;
+}
+
+QString MakefileGenerator::buildArgs(const QString &outdir)
+{
+ QString ret;
+ //special variables
+ if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
+ ret += " QMAKE_ABSOLUTE_SOURCE_PATH=" + escapeFilePath(project->first("QMAKE_ABSOLUTE_SOURCE_PATH"));
+
+ //warnings
+ else if(Option::warn_level == WarnNone)
+ ret += " -Wnone";
+ else if(Option::warn_level == WarnAll)
+ ret += " -Wall";
+ else if(Option::warn_level & WarnParser)
+ ret += " -Wparser";
+ //other options
+ if(!Option::user_template.isEmpty())
+ ret += " -t " + Option::user_template;
+ if(!Option::user_template_prefix.isEmpty())
+ ret += " -tp " + Option::user_template_prefix;
+ if(!Option::mkfile::do_cache)
+ ret += " -nocache";
+ if(!Option::mkfile::do_deps)
+ ret += " -nodepend";
+ if(!Option::mkfile::do_dep_heuristics)
+ ret += " -nodependheuristics";
+ if(!Option::mkfile::qmakespec_commandline.isEmpty())
+ ret += " -spec " + specdir(outdir);
+ if (Option::target_mode_overridden) {
+ if (Option::target_mode == Option::TARG_MACX_MODE)
+ ret += " -macx";
+ else if (Option::target_mode == Option::TARG_UNIX_MODE)
+ ret += " -unix";
+ else if (Option::target_mode == Option::TARG_WIN_MODE)
+ ret += " -win32";
+ }
+
+ //configs
+ for(QStringList::Iterator it = Option::user_configs.begin();
+ it != Option::user_configs.end(); ++it)
+ ret += " -config " + (*it);
+ //arguments
+ for(QStringList::Iterator it = Option::before_user_vars.begin();
+ it != Option::before_user_vars.end(); ++it) {
+ if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
+ ret += " " + escapeFilePath((*it));
+ }
+ if(Option::after_user_vars.count()) {
+ ret += " -after ";
+ for(QStringList::Iterator it = Option::after_user_vars.begin();
+ it != Option::after_user_vars.end(); ++it) {
+ if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
+ ret += " " + escapeFilePath((*it));
+ }
+ }
+ return ret;
+}
+
+//could get stored argv, but then it would have more options than are
+//probably necesary this will try to guess the bare minimum..
+QString MakefileGenerator::build_args(const QString &outdir)
+{
+ QString ret = "$(QMAKE)";
+
+ // general options and arguments
+ ret += buildArgs(outdir);
+
+ //output
+ QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
+ if(!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE"))
+ ret += " -o " + escapeFilePath(ofile);
+
+ //inputs
+ ret += " " + escapeFilePath(fileFixify(project->projectFile(), outdir));
+
+ return ret;
+}
+
+void
+MakefileGenerator::writeHeader(QTextStream &t)
+{
+ t << "#############################################################################" << endl;
+ t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
+ t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+ t << QDateTime::currentDateTime().toString() << endl;
+ t << "# Project: " << fileFixify(project->projectFile()) << endl;
+ t << "# Template: " << var("TEMPLATE") << endl;
+ if(!project->isActiveConfig("build_pass"))
+ t << "# Command: " << build_args().replace("$(QMAKE)", var("QMAKE_QMAKE")) << endl;
+ t << "#############################################################################" << endl;
+ t << endl;
+}
+
+QList<MakefileGenerator::SubTarget*>
+MakefileGenerator::findSubDirsSubTargets() const
+{
+ QList<SubTarget*> targets;
+ {
+ const QStringList subdirs = project->values("SUBDIRS");
+ for(int subdir = 0; subdir < subdirs.size(); ++subdir) {
+ QString fixedSubdir = subdirs[subdir];
+ fixedSubdir = fixedSubdir.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
+
+ SubTarget *st = new SubTarget;
+ st->name = subdirs[subdir];
+ targets.append(st);
+
+ bool fromFile = false;
+ QString file = subdirs[subdir];
+ if(!project->isEmpty(fixedSubdir + ".file")) {
+ if(!project->isEmpty(fixedSubdir + ".subdir"))
+ warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s",
+ subdirs[subdir].toLatin1().constData());
+ file = project->first(fixedSubdir + ".file");
+ fromFile = true;
+ } else if(!project->isEmpty(fixedSubdir + ".subdir")) {
+ file = project->first(fixedSubdir + ".subdir");
+ fromFile = false;
+ } else {
+ fromFile = file.endsWith(Option::pro_ext);
+ }
+ file = Option::fixPathToTargetOS(file);
+
+ if(fromFile) {
+ int slsh = file.lastIndexOf(Option::dir_sep);
+ if(slsh != -1) {
+ st->in_directory = file.left(slsh+1);
+ st->profile = file.mid(slsh+1);
+ } else {
+ st->profile = file;
+ }
+ } else {
+ if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro"))
+ st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext;
+ st->in_directory = file;
+ }
+ while(st->in_directory.endsWith(Option::dir_sep))
+ st->in_directory.chop(1);
+ if(fileInfo(st->in_directory).isRelative())
+ st->out_directory = st->in_directory;
+ else
+ st->out_directory = fileFixify(st->in_directory, qmake_getpwd(), Option::output_dir);
+ if(!project->isEmpty(fixedSubdir + ".makefile")) {
+ st->makefile = project->first(fixedSubdir + ".makefile");
+ } else {
+ st->makefile = "$(MAKEFILE)";
+ if(!st->profile.isEmpty()) {
+ QString basename = st->in_directory;
+ int new_slsh = basename.lastIndexOf(Option::dir_sep);
+ if(new_slsh != -1)
+ basename = basename.mid(new_slsh+1);
+ if(st->profile != basename + Option::pro_ext)
+ st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length());
+ }
+ }
+ if(!project->isEmpty(fixedSubdir + ".depends")) {
+ const QStringList depends = project->values(fixedSubdir + ".depends");
+ for(int depend = 0; depend < depends.size(); ++depend) {
+ bool found = false;
+ for(int subDep = 0; subDep < subdirs.size(); ++subDep) {
+ if(subdirs[subDep] == depends.at(depend)) {
+ QString fixedSubDep = subdirs[subDep];
+ fixedSubDep = fixedSubDep.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
+ if(!project->isEmpty(fixedSubDep + ".target")) {
+ st->depends += project->first(fixedSubDep + ".target");
+ } else {
+ QString d = Option::fixPathToLocalOS(subdirs[subDep]);
+ if(!project->isEmpty(fixedSubDep + ".file"))
+ d = project->first(fixedSubDep + ".file");
+ else if(!project->isEmpty(fixedSubDep + ".subdir"))
+ d = project->first(fixedSubDep + ".subdir");
+ st->depends += "sub-" + d.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
+ }
+ found = true;
+ break;
+ }
+ }
+ if(!found) {
+ QString depend_str = depends.at(depend);
+ st->depends += depend_str.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
+ }
+ }
+ }
+ if(!project->isEmpty(fixedSubdir + ".target")) {
+ st->target = project->first(fixedSubdir + ".target");
+ } else {
+ st->target = "sub-" + file;
+ st->target = st->target.replace(QRegExp("[^a-zA-Z0-9_]"),"-");
+ }
+ }
+ }
+ return targets;
+}
+
+void
+MakefileGenerator::writeSubDirs(QTextStream &t)
+{
+ QList<SubTarget*> targets = findSubDirsSubTargets();
+ t << "first: make_default" << endl;
+ int flags = SubTargetInstalls;
+ if(project->isActiveConfig("ordered"))
+ flags |= SubTargetOrdered;
+ writeSubTargets(t, targets, flags);
+ qDeleteAll(targets);
+}
+
+void
+MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags)
+{
+ // blasted includes
+ QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
+ for(QStringList::Iterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it)
+ t << "include " << (*qeui_it) << endl;
+
+ if (!(flags & SubTargetSkipDefaultVariables)) {
+ QString ofile = Option::fixPathToTargetOS(Option::output.fileName());
+ if(ofile.lastIndexOf(Option::dir_sep) != -1)
+ ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1);
+ t << "MAKEFILE = " << ofile << endl;
+ /* Calling Option::fixPathToTargetOS() is necessary for MinGW/MSYS, which requires
+ * back-slashes to be turned into slashes. */
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
+ t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
+ t << "COPY = " << var("QMAKE_COPY") << endl;
+ t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl;
+ t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl;
+ t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl;
+ t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
+ t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl;
+ t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl;
+ t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
+ t << "MOVE = " << var("QMAKE_MOVE") << endl;
+ t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
+ t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
+ t << "SUBTARGETS = "; // subtargets are sub-directory
+ for(int target = 0; target < targets.size(); ++target)
+ t << " \\\n\t\t" << targets.at(target)->target;
+ t << endl << endl;
+ }
+ writeExtraVariables(t);
+
+ QStringList targetSuffixes;
+ const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH");
+ if (!(flags & SubTargetSkipDefaultTargets)) {
+ targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean"
+ << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install")
+ << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall");
+ }
+
+ // generate target rules
+ for(int target = 0; target < targets.size(); ++target) {
+ SubTarget *subtarget = targets.at(target);
+ QString in_directory = subtarget->in_directory;
+ if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
+ in_directory += Option::dir_sep;
+ QString out_directory = subtarget->out_directory;
+ if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
+ out_directory += Option::dir_sep;
+ if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
+ out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
+
+ QString mkfile = subtarget->makefile;
+ if(!in_directory.isEmpty())
+ mkfile.prepend(out_directory);
+
+ QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout;
+#define MAKE_CD_IN_AND_OUT(directory) \
+ if(!directory.isEmpty()) { \
+ if(project->isActiveConfig("cd_change_global")) { \
+ directory ## _cdin = "\n\tcd " + directory + "\n\t"; \
+ QDir pwd(Option::output_dir); \
+ QStringList in = directory.split(Option::dir_sep), out; \
+ for(int i = 0; i < in.size(); i++) { \
+ if(in.at(i) == "..") \
+ out.prepend(fileInfo(pwd.path()).fileName()); \
+ else if(in.at(i) != ".") \
+ out.prepend(".."); \
+ pwd.cd(in.at(i)); \
+ } \
+ directory ## _cdout = "\n\t@cd " + out.join(Option::dir_sep); \
+ } else { \
+ directory ## _cdin = "\n\tcd " + directory + " && "; \
+ } \
+ } else { \
+ directory ## _cdin = "\n\t"; \
+ }
+ MAKE_CD_IN_AND_OUT(in_directory);
+ MAKE_CD_IN_AND_OUT(out_directory);
+
+ //qmake it
+ if(!subtarget->profile.isEmpty()) {
+ QString out = subtarget->makefile;
+ QString in = fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute);
+ if(out.startsWith(in_directory))
+ out = out.mid(in_directory.length());
+ t << mkfile << ": " << "\n\t";
+ if(!in_directory.isEmpty()) {
+ t << mkdir_p_asstring(out_directory)
+ << out_directory_cdin
+ << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
+ << in_directory_cdout << endl;
+ } else {
+ t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
+ }
+ t << subtarget->target << "-qmake_all: ";
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\t";
+ if(!in_directory.isEmpty()) {
+ t << mkdir_p_asstring(out_directory)
+ << out_directory_cdin
+ << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out
+ << in_directory_cdout << endl;
+ } else {
+ t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl;
+ }
+ }
+
+ QString makefilein = " -f " + subtarget->makefile;
+
+ { //actually compile
+ t << subtarget->target << ": " << mkfile;
+ if(!subtarget->depends.isEmpty())
+ t << " " << valList(subtarget->depends);
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << out_directory_cdin
+ << "$(MAKE)" << makefilein
+ << out_directory_cdout << endl;
+ }
+
+ for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) {
+ QString s = targetSuffixes.at(suffix);
+ if(s == "install_subtargets")
+ s = "install";
+ else if(s == "uninstall_subtargets")
+ s = "uninstall";
+ else if(s == "make_first")
+ s = "first";
+ else if(s == "make_default")
+ s = QString();
+
+ if(flags & SubTargetOrdered) {
+ t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile;
+ if(target)
+ t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered ";
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << out_directory_cdin
+ << "$(MAKE)" << makefilein << " " << s
+ << out_directory_cdout << endl;
+ }
+ t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile;
+ if(!subtarget->depends.isEmpty())
+ t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ",
+ "-"+targetSuffixes.at(suffix));
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << out_directory_cdin
+ << "$(MAKE)" << makefilein << " " << s
+ << out_directory_cdout << endl;
+ }
+ }
+ t << endl;
+
+ if (!(flags & SubTargetSkipDefaultTargets)) {
+ if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1)
+ project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all");
+
+ writeMakeQmake(t);
+
+ t << "qmake_all:";
+ if(!targets.isEmpty()) {
+ for(QList<SubTarget*>::Iterator it = targets.begin(); it != targets.end(); ++it) {
+ if(!(*it)->profile.isEmpty())
+ t << " " << (*it)->target << "-" << "qmake_all";
+ }
+ }
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ if(project->isActiveConfig("no_empty_targets"))
+ t << "\n\t" << "@cd .";
+ t << endl << endl;
+ }
+
+ for(int s = 0; s < targetSuffixes.size(); ++s) {
+ QString suffix = targetSuffixes.at(s);
+ if(!(flags & SubTargetInstalls) && suffix.endsWith("install"))
+ continue;
+
+ t << suffix << ":";
+ for(int target = 0; target < targets.size(); ++target) {
+ SubTarget *subTarget = targets.at(target);
+ if((suffix == "make_first" || suffix == "make_default")
+ && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) {
+ continue;
+ }
+ QString targetRule = subTarget->target + "-" + suffix;
+ if(flags & SubTargetOrdered)
+ targetRule += "-ordered";
+ t << " " << targetRule;
+ }
+ if(suffix == "all" || suffix == "make_first")
+ t << varGlue("ALL_DEPS"," "," ","");
+ if(suffix == "clean")
+ t << varGlue("CLEAN_DEPS"," "," ","");
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << endl;
+ if(suffix == "clean") {
+ t << varGlue("QMAKE_CLEAN","\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ", "\n");
+ } else if(suffix == "distclean") {
+ QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
+ if(!ofile.isEmpty())
+ t << "\t-$(DEL_FILE) " << ofile << endl;
+ t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n");
+ } else if(project->isActiveConfig("no_empty_targets")) {
+ t << "\t" << "@cd ." << endl;
+ }
+ }
+
+ // user defined targets
+ QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
+ for(QStringList::Iterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) {
+ QString targ = var((*qut_it) + ".target"),
+ cmd = var((*qut_it) + ".commands"), deps;
+ if(targ.isEmpty())
+ targ = (*qut_it);
+ t << endl;
+
+ QStringList &deplist = project->values((*qut_it) + ".depends");
+ for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) {
+ QString dep = var((*dep_it) + ".target");
+ if(dep.isEmpty())
+ dep = Option::fixPathToTargetOS(*dep_it, false);
+ deps += " " + dep;
+ }
+ if(project->values((*qut_it) + ".CONFIG").indexOf("recursive") != -1) {
+ QSet<QString> recurse;
+ if(project->isSet((*qut_it) + ".recurse")) {
+ recurse = project->values((*qut_it) + ".recurse").toSet();
+ } else {
+ for(int target = 0; target < targets.size(); ++target)
+ recurse.insert(targets.at(target)->name);
+ }
+ for(int target = 0; target < targets.size(); ++target) {
+ SubTarget *subtarget = targets.at(target);
+ QString in_directory = subtarget->in_directory;
+ if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep))
+ in_directory += Option::dir_sep;
+ QString out_directory = subtarget->out_directory;
+ if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep))
+ out_directory += Option::dir_sep;
+ if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path))
+ out_directory = Option::output_dir + out_directory.mid(abs_source_path.length());
+
+ if(!recurse.contains(subtarget->name))
+ continue;
+ QString mkfile = subtarget->makefile;
+ if(!in_directory.isEmpty()) {
+ if(!out_directory.endsWith(Option::dir_sep))
+ mkfile.prepend(out_directory + Option::dir_sep);
+ else
+ mkfile.prepend(out_directory);
+ }
+ QString out_directory_cdin, out_directory_cdout;
+ MAKE_CD_IN_AND_OUT(out_directory);
+
+ //don't need the makefile arg if it isn't changed
+ QString makefilein;
+ if(subtarget->makefile != "$(MAKEFILE)")
+ makefilein = " -f " + subtarget->makefile;
+
+ //write the rule/depends
+ if(flags & SubTargetOrdered) {
+ const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered";
+ t << dep << ": " << mkfile;
+ if(target)
+ t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered ";
+ deps += " " + dep;
+ } else {
+ const QString dep = subtarget->target + "-" + (*qut_it);
+ t << dep << ": " << mkfile;
+ if(!subtarget->depends.isEmpty())
+ t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it));
+ deps += " " + dep;
+ }
+
+ QString sub_targ = targ;
+ if(project->isSet((*qut_it) + ".recurse_target"))
+ sub_targ = project->first((*qut_it) + ".recurse_target");
+
+ //write the commands
+ if(!out_directory.isEmpty()) {
+ t << out_directory_cdin
+ << "$(MAKE)" << makefilein << " " << sub_targ
+ << out_directory_cdout << endl;
+ } else {
+ t << "\n\t"
+ << "$(MAKE)" << makefilein << " " << sub_targ << endl;
+ }
+ }
+ }
+ if(project->isEmpty("QMAKE_NOFORCE") &&
+ project->values((*qut_it) + ".CONFIG").indexOf("phony") != -1)
+ deps += " FORCE";
+ t << targ << ":" << deps << "\n";
+ if(!cmd.isEmpty())
+ t << "\t" << cmd << endl;
+ }
+
+ if(flags & SubTargetInstalls) {
+ project->values("INSTALLDEPS") += "install_subtargets";
+ project->values("UNINSTALLDEPS") += "uninstall_subtargets";
+ writeInstalls(t, "INSTALLS", true);
+ }
+
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << "FORCE:" << endl << endl;
+}
+
+void
+MakefileGenerator::writeMakeQmake(QTextStream &t)
+{
+ QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName()));
+ if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
+ QStringList files = fileFixify(Option::mkfile::project_files);
+ t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE")) << ": " << "\n\t"
+ << "@$(QMAKE) -prl " << buildArgs() << " " << files.join(" ") << endl;
+ }
+
+ QString pfile = project->projectFile();
+ if(pfile != "(stdin)") {
+ QString qmake = build_args();
+ if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) {
+ t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " ";
+ if(Option::mkfile::do_cache)
+ t << escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " ";
+ if(!specdir().isEmpty()) {
+ if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf")))
+ t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " ";
+ }
+ const QStringList &included = project->values("QMAKE_INTERNAL_INCLUDED_FILES");
+ t << escapeDependencyPaths(included).join(" \\\n\t\t") << "\n\t"
+ << qmake << endl;
+ for(int include = 0; include < included.size(); ++include) {
+ const QString i(included.at(include));
+ if(!i.isEmpty())
+ t << i << ":" << endl;
+ }
+ }
+ if(project->first("QMAKE_ORIG_TARGET") != "qmake") {
+ t << "qmake: " <<
+ project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t");
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << " FORCE";
+ t << "\n\t" << "@" << qmake << endl << endl;
+ }
+ }
+}
+
+QFileInfo
+MakefileGenerator::fileInfo(QString file) const
+{
+ static QHash<FileInfoCacheKey, QFileInfo> *cache = 0;
+ static QFileInfo noInfo = QFileInfo();
+ if(!cache) {
+ cache = new QHash<FileInfoCacheKey, QFileInfo>;
+ qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FileInfoCacheKey, QFileInfo> >, (void**)&cache);
+ }
+ FileInfoCacheKey cacheKey(file);
+ QFileInfo value = cache->value(cacheKey, noInfo);
+ if (value != noInfo)
+ return value;
+
+ QFileInfo fi(file);
+ if (fi.exists())
+ cache->insert(cacheKey, fi);
+ return fi;
+}
+
+QString
+MakefileGenerator::unescapeFilePath(const QString &path) const
+{
+ QString ret = path;
+ if(!ret.isEmpty()) {
+ if(ret.contains(QLatin1String("\\ ")))
+ ret.replace(QLatin1String("\\ "), QLatin1String(" "));
+ if(ret.contains(QLatin1Char('\"')))
+ ret.remove(QLatin1Char('\"'));
+ }
+ return ret;
+}
+
+QStringList
+MakefileGenerator::escapeFilePaths(const QStringList &paths) const
+{
+ QStringList ret;
+ for(int i = 0; i < paths.size(); ++i)
+ ret.append(escapeFilePath(paths.at(i)));
+ return ret;
+}
+
+QStringList
+MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const
+{
+ QStringList ret;
+ for(int i = 0; i < paths.size(); ++i)
+ ret.append(escapeDependencyPath(paths.at(i)));
+ return ret;
+}
+
+QStringList
+MakefileGenerator::unescapeFilePaths(const QStringList &paths) const
+{
+ QStringList ret;
+ for(int i = 0; i < paths.size(); ++i)
+ ret.append(unescapeFilePath(paths.at(i)));
+ return ret;
+}
+
+QStringList
+MakefileGenerator::fileFixify(const QStringList& files, const QString &out_dir, const QString &in_dir,
+ FileFixifyType fix, bool canon) const
+{
+ if(files.isEmpty())
+ return files;
+ QStringList ret;
+ for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
+ if(!(*it).isEmpty())
+ ret << fileFixify((*it), out_dir, in_dir, fix, canon);
+ }
+ return ret;
+}
+
+QString
+MakefileGenerator::fileFixify(const QString& file, const QString &out_d, const QString &in_d,
+ FileFixifyType fix, bool canon) const
+{
+ if(file.isEmpty())
+ return file;
+ QString ret = unescapeFilePath(file);
+
+ //do the fixin'
+ QString orig_file = ret;
+ if(ret.startsWith(QLatin1Char('~'))) {
+ if(ret.startsWith(QLatin1String("~/")))
+ ret = QDir::homePath() + ret.mid(1);
+ else
+ warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData());
+ }
+ if(fix == FileFixifyAbsolute || (fix == FileFixifyDefault && project->isActiveConfig("no_fixpath"))) {
+ if(fix == FileFixifyAbsolute && QDir::isRelativePath(ret)) { //already absolute
+ QString pwd = qmake_getpwd();
+ if (!pwd.endsWith(QLatin1Char('/')))
+ pwd += QLatin1Char('/');
+ ret.prepend(pwd);
+ }
+ ret = Option::fixPathToTargetOS(ret, false, canon);
+ } else { //fix it..
+ QString out_dir = QDir(Option::output_dir).absoluteFilePath(out_d);
+ QString in_dir = QDir(qmake_getpwd()).absoluteFilePath(in_d);
+ {
+ QFileInfo in_fi(fileInfo(in_dir));
+ if(in_fi.exists())
+ in_dir = in_fi.canonicalFilePath();
+ QFileInfo out_fi(fileInfo(out_dir));
+ if(out_fi.exists())
+ out_dir = out_fi.canonicalFilePath();
+ }
+
+ QString qfile(Option::fixPathToLocalOS(ret, true, canon));
+ QFileInfo qfileinfo(fileInfo(qfile));
+ if(out_dir != in_dir || !qfileinfo.isRelative()) {
+ if(qfileinfo.isRelative()) {
+ ret = in_dir + "/" + qfile;
+ qfileinfo.setFile(ret);
+ }
+ ret = Option::fixPathToTargetOS(ret, false, canon);
+ if(canon && qfileinfo.exists() &&
+ file == Option::fixPathToTargetOS(ret, true, canon))
+ ret = Option::fixPathToTargetOS(qfileinfo.canonicalFilePath());
+ QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon);
+ if(ret == match_dir) {
+ ret = "";
+ } else if(ret.startsWith(match_dir + Option::dir_sep)) {
+ ret = ret.mid(match_dir.length() + Option::dir_sep.length());
+ } else {
+ //figure out the depth
+ int depth = 4;
+ if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
+ Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
+ if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH"))
+ depth = project->first("QMAKE_PROJECT_DEPTH").toInt();
+ else if(Option::mkfile::cachefile_depth != -1)
+ depth = Option::mkfile::cachefile_depth;
+ }
+ //calculate how much can be removed
+ QString dot_prefix;
+ for(int i = 1; i <= depth; i++) {
+ int sl = match_dir.lastIndexOf(Option::dir_sep);
+ if(sl == -1)
+ break;
+ match_dir = match_dir.left(sl);
+ if(match_dir.isEmpty())
+ break;
+ if(ret.startsWith(match_dir + Option::dir_sep)) {
+ //concat
+ int remlen = ret.length() - (match_dir.length() + 1);
+ if(remlen < 0)
+ remlen = 0;
+ ret = ret.right(remlen);
+ //prepend
+ for(int o = 0; o < i; o++)
+ dot_prefix += ".." + Option::dir_sep;
+ break;
+ }
+ }
+ ret.prepend(dot_prefix);
+ }
+ } else {
+ ret = Option::fixPathToTargetOS(ret, false, canon);
+ }
+ }
+ if(ret.isEmpty())
+ ret = ".";
+ debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, canon, orig_file.toLatin1().constData(),
+ ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(),
+ qmake_getpwd().toLatin1().constData(), Option::output_dir.toLatin1().constData());
+ return ret;
+}
+
+void
+MakefileGenerator::checkMultipleDefinition(const QString &f, const QString &w)
+{
+ if(!(Option::warn_level & WarnLogic))
+ return;
+ QString file = f;
+ int slsh = f.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ file.remove(0, slsh + 1);
+ QStringList &l = project->values(w);
+ for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
+ QString file2((*val_it));
+ slsh = file2.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ file2.remove(0, slsh + 1);
+ if(file2 == file) {
+ warn_msg(WarnLogic, "Found potential symbol conflict of %s (%s) in %s",
+ file.toLatin1().constData(), (*val_it).toLatin1().constData(), w.toLatin1().constData());
+ break;
+ }
+ }
+}
+
+QMakeLocalFileName
+MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen)
+{
+ if(forOpen)
+ return QMakeLocalFileName(fileFixify(file.real(), qmake_getpwd(), Option::output_dir));
+ return QMakeLocalFileName(fileFixify(file.real()));
+}
+
+QFileInfo
+MakefileGenerator::findFileInfo(const QMakeLocalFileName &file)
+{
+ return fileInfo(file.local());
+}
+
+QMakeLocalFileName
+MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file)
+{
+ QMakeLocalFileName ret;
+ if(!project->isEmpty("SKIP_DEPENDS")) {
+ bool found = false;
+ QStringList &nodeplist = project->values("SKIP_DEPENDS");
+ for(QStringList::Iterator it = nodeplist.begin();
+ it != nodeplist.end(); ++it) {
+ QRegExp regx((*it));
+ if(regx.indexIn(dep.local()) != -1) {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ return ret;
+ }
+
+ ret = QMakeSourceFileInfo::findFileForDep(dep, file);
+ if(!ret.isNull())
+ return ret;
+
+ //these are some "hacky" heuristics it will try to do on an include
+ //however these can be turned off at runtime, I'm not sure how
+ //reliable these will be, most likely when problems arise turn it off
+ //and see if they go away..
+ if(Option::mkfile::do_dep_heuristics) {
+ if(depHeuristicsCache.contains(dep.real()))
+ return depHeuristicsCache[dep.real()];
+
+ if(Option::output_dir != qmake_getpwd()
+ && QDir::isRelativePath(dep.real())) { //is it from the shadow tree
+ QList<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths();
+ depdirs.prepend(fileInfo(file.real()).absoluteDir().path());
+ QString pwd = qmake_getpwd();
+ if(pwd.at(pwd.length()-1) != '/')
+ pwd += '/';
+ for(int i = 0; i < depdirs.count(); i++) {
+ QString dir = depdirs.at(i).real();
+ if(!QDir::isRelativePath(dir) && dir.startsWith(pwd))
+ dir = dir.mid(pwd.length());
+ if(QDir::isRelativePath(dir)) {
+ if(!dir.endsWith(Option::dir_sep))
+ dir += Option::dir_sep;
+ QString shadow = fileFixify(dir + dep.local(), pwd, Option::output_dir);
+ if(exists(shadow)) {
+ ret = QMakeLocalFileName(shadow);
+ goto found_dep_from_heuristic;
+ }
+ }
+ }
+ }
+ { //is it from an EXTRA_TARGET
+ const QString dep_basename = dep.local().section(Option::dir_sep, -1);
+ QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
+ for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) {
+ QString targ = var((*it) + ".target");
+ if(targ.isEmpty())
+ targ = (*it);
+ QString out = Option::fixPathToTargetOS(targ);
+ if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
+ ret = QMakeLocalFileName(out);
+ goto found_dep_from_heuristic;
+ }
+ }
+ }
+ { //is it from an EXTRA_COMPILER
+ const QString dep_basename = dep.local().section(Option::dir_sep, -1);
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = project->values((*it) + ".output").first();
+ if(tmp_out.isEmpty())
+ continue;
+ QStringList &tmp = project->values((*it) + ".input");
+ for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
+ QStringList &inputs = project->values((*it2));
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
+ QString out = Option::fixPathToTargetOS(unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString())));
+ if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) {
+ ret = QMakeLocalFileName(fileFixify(out, qmake_getpwd(), Option::output_dir));
+ goto found_dep_from_heuristic;
+ }
+ }
+ }
+ }
+ }
+ found_dep_from_heuristic:
+ depHeuristicsCache.insert(dep.real(), ret);
+ }
+ return ret;
+}
+
+QStringList
+&MakefileGenerator::findDependencies(const QString &file)
+{
+ const QString fixedFile = fileFixify(file);
+ if(!dependsCache.contains(fixedFile)) {
+#if 1
+ QStringList deps = QMakeSourceFileInfo::dependencies(file);
+ if(file != fixedFile)
+ deps += QMakeSourceFileInfo::dependencies(fixedFile);
+#else
+ QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile);
+#endif
+ dependsCache.insert(fixedFile, deps);
+ }
+ return dependsCache[fixedFile];
+}
+
+QString
+MakefileGenerator::specdir(const QString &outdir)
+{
+#if 0
+ if(!spec.isEmpty())
+ return spec;
+#endif
+ spec = fileFixify(Option::mkfile::qmakespec, outdir);
+ return spec;
+}
+
+bool
+MakefileGenerator::openOutput(QFile &file, const QString &build) const
+{
+ {
+ QString outdir;
+ if(!file.fileName().isEmpty()) {
+ if(QDir::isRelativePath(file.fileName()))
+ file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run
+ QFileInfo fi(fileInfo(file.fileName()));
+ if(fi.isDir())
+ outdir = file.fileName() + '/';
+ }
+ if(!outdir.isEmpty() || file.fileName().isEmpty()) {
+ QString fname = "Makefile";
+ if(!project->isEmpty("MAKEFILE"))
+ fname = project->first("MAKEFILE");
+ file.setFileName(outdir + fname);
+ }
+ }
+ if(QDir::isRelativePath(file.fileName())) {
+ QString fname = Option::output_dir; //pwd when qmake was run
+ if(!fname.endsWith("/"))
+ fname += "/";
+ fname += file.fileName();
+ file.setFileName(fname);
+ }
+ if(!build.isEmpty())
+ file.setFileName(file.fileName() + "." + build);
+ if(project->isEmpty("QMAKE_MAKEFILE"))
+ project->values("QMAKE_MAKEFILE").append(file.fileName());
+ int slsh = file.fileName().lastIndexOf('/');
+ if(slsh != -1)
+ mkdir(file.fileName().left(slsh));
+ if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
+ QFileInfo fi(fileInfo(Option::output.fileName()));
+ QString od;
+ if(fi.isSymLink())
+ od = fileInfo(fi.readLink()).absolutePath();
+ else
+ od = fi.path();
+ od = QDir::fromNativeSeparators(od);
+ if(QDir::isRelativePath(od)) {
+ QString dir = Option::output_dir;
+ if (!dir.endsWith('/') && !od.isEmpty())
+ dir += '/';
+ od.prepend(dir);
+ }
+ Option::output_dir = od;
+ return true;
+ }
+ return false;
+}
+
+QString
+MakefileGenerator::pkgConfigFileName(bool fixify)
+{
+ QString ret = var("TARGET");
+ int slsh = ret.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ ret = ret.right(ret.length() - slsh - 1);
+ if(ret.startsWith("lib"))
+ ret = ret.mid(3);
+ int dot = ret.indexOf('.');
+ if(dot != -1)
+ ret = ret.left(dot);
+ ret += Option::pkgcfg_ext;
+ QString subdir = project->first("QMAKE_PKGCONFIG_DESTDIR");
+ if(!subdir.isEmpty()) {
+ // initOutPaths() appends dir_sep, but just to be safe..
+ if (!subdir.endsWith(Option::dir_sep))
+ ret.prepend(Option::dir_sep);
+ ret.prepend(subdir);
+ }
+ if(fixify) {
+ if(QDir::isRelativePath(ret) && !project->isEmpty("DESTDIR"))
+ ret.prepend(project->first("DESTDIR"));
+ ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir));
+ }
+ return ret;
+}
+
+QString
+MakefileGenerator::pkgConfigPrefix() const
+{
+ if(!project->isEmpty("QMAKE_PKGCONFIG_PREFIX"))
+ return project->first("QMAKE_PKGCONFIG_PREFIX");
+ return QLibraryInfo::location(QLibraryInfo::PrefixPath);
+}
+
+QString
+MakefileGenerator::pkgConfigFixPath(QString path) const
+{
+ QString prefix = pkgConfigPrefix();
+ if(path.startsWith(prefix))
+ path = path.replace(prefix, "${prefix}");
+ return path;
+}
+
+void
+MakefileGenerator::writePkgConfigFile()
+{
+ QString fname = pkgConfigFileName(), lname = fname;
+ mkdir(fileInfo(fname).path());
+ int slsh = lname.lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ lname = lname.right(lname.length() - slsh - 1);
+ QFile ft(fname);
+ if(!ft.open(QIODevice::WriteOnly))
+ return;
+ project->values("ALL_DEPS").append(fileFixify(fname));
+ QTextStream t(&ft);
+
+ QString prefix = pkgConfigPrefix();
+ QString libDir = project->first("QMAKE_PKGCONFIG_LIBDIR");
+ if(libDir.isEmpty())
+ libDir = prefix + Option::dir_sep + "lib" + Option::dir_sep;
+ QString includeDir = project->first("QMAKE_PKGCONFIG_INCDIR");
+ if(includeDir.isEmpty())
+ includeDir = prefix + "/include";
+
+ t << "prefix=" << prefix << endl;
+ t << "exec_prefix=${prefix}\n"
+ << "libdir=" << pkgConfigFixPath(libDir) << "\n"
+ << "includedir=" << pkgConfigFixPath(includeDir) << endl;
+ // non-standard entry. Provides useful info normally only
+ // contained in the internal .qmake.cache file
+ t << varGlue("CONFIG", "qt_config=", " ", "") << endl;
+
+ //extra PKGCONFIG variables
+ const QStringList &pkgconfig_vars = project->values("QMAKE_PKGCONFIG_VARIABLES");
+ for(int i = 0; i < pkgconfig_vars.size(); ++i) {
+ QString var = project->first(pkgconfig_vars.at(i) + ".name"),
+ val = project->values(pkgconfig_vars.at(i) + ".value").join(" ");
+ if(var.isEmpty())
+ continue;
+ if(val.isEmpty()) {
+ const QStringList &var_vars = project->values(pkgconfig_vars.at(i) + ".variable");
+ for(int v = 0; v < var_vars.size(); ++v) {
+ const QStringList &vars = project->values(var_vars.at(v));
+ for(int var = 0; var < vars.size(); ++var) {
+ if(!val.isEmpty())
+ val += " ";
+ val += pkgConfigFixPath(vars.at(var));
+ }
+ }
+ }
+ t << var << "=" << val << endl;
+ }
+
+ t << endl;
+
+ QString name = project->first("QMAKE_PKGCONFIG_NAME");
+ if(name.isEmpty()) {
+ name = project->first("QMAKE_ORIG_TARGET").toLower();
+ name.replace(0, 1, name[0].toUpper());
+ }
+ t << "Name: " << name << endl;
+ QString desc = project->values("QMAKE_PKGCONFIG_DESCRIPTION").join(" ");
+ if(desc.isEmpty()) {
+ if(name.isEmpty()) {
+ desc = project->first("QMAKE_ORIG_TARGET").toLower();
+ desc.replace(0, 1, desc[0].toUpper());
+ } else {
+ desc = name;
+ }
+ if(project->first("TEMPLATE") == "lib") {
+ if(project->isActiveConfig("plugin"))
+ desc += " Plugin";
+ else
+ desc += " Library";
+ } else if(project->first("TEMPLATE") == "app") {
+ desc += " Application";
+ }
+ }
+ t << "Description: " << desc << endl;
+ t << "Version: " << project->first("VERSION") << endl;
+
+ // libs
+ t << "Libs: ";
+ QString pkgConfiglibDir;
+ QString pkgConfiglibName;
+ if (Option::target_mode == Option::TARG_MACX_MODE && project->isActiveConfig("lib_bundle")) {
+ pkgConfiglibDir = "-F${libdir}";
+ QString bundle;
+ if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
+ bundle = unescapeFilePath(project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"));
+ else
+ bundle = unescapeFilePath(project->first("TARGET"));
+ int suffix = bundle.lastIndexOf(".framework");
+ if (suffix != -1)
+ bundle = bundle.left(suffix);
+ pkgConfiglibName = "-framework " + bundle + " ";
+ } else {
+ pkgConfiglibDir = "-L${libdir}";
+ pkgConfiglibName = "-l" + lname.left(lname.length()-Option::libtool_ext.length());
+ }
+ t << pkgConfiglibDir << " " << pkgConfiglibName << " " << endl;
+
+ QStringList libs;
+ if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) {
+ libs = project->values("QMAKE_INTERNAL_PRL_LIBS");
+ } else {
+ libs << "QMAKE_LIBS"; //obvious one
+ }
+ libs << "QMAKE_LIBS_PRIVATE";
+ libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread?
+ t << "Libs.private: ";
+ for(QStringList::ConstIterator it = libs.begin(); it != libs.end(); ++it) {
+ t << project->values((*it)).join(" ") << " ";
+ }
+ t << endl;
+
+ // flags
+ // ### too many
+ t << "Cflags: "
+ // << var("QMAKE_CXXFLAGS") << " "
+ << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << project->values("PRL_EXPORT_CXXFLAGS").join(" ")
+ << project->values("QMAKE_PKGCONFIG_CFLAGS").join(" ")
+ // << varGlue("DEFINES","-D"," -D"," ")
+ << " -I${includedir}" << endl;
+
+ // requires
+ const QString requires = project->values("QMAKE_PKGCONFIG_REQUIRES").join(" ");
+ if (!requires.isEmpty()) {
+ t << "Requires: " << requires << endl;
+ }
+
+ t << endl;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h
new file mode 100644
index 0000000000..e0ef52d251
--- /dev/null
+++ b/qmake/generators/makefile.h
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAKEFILE_H
+#define MAKEFILE_H
+
+#include "option.h"
+#include "project.h"
+#include "makefiledeps.h"
+#include <qtextstream.h>
+#include <qlist.h>
+#include <qhash.h>
+#include <qfileinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+struct ReplaceExtraCompilerCacheKey
+{
+ mutable uint hash;
+ QString var, in, out, pwd;
+ ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o);
+ bool operator==(const ReplaceExtraCompilerCacheKey &f) const;
+ inline uint hashCode() const {
+ if(!hash)
+ hash = qHash(var) | qHash(in) | qHash(out) /*| qHash(pwd)*/;
+ return hash;
+ }
+};
+inline uint qHash(const ReplaceExtraCompilerCacheKey &f) { return f.hashCode(); }
+
+struct ReplaceExtraCompilerCacheKey;
+
+class MakefileGenerator : protected QMakeSourceFileInfo
+{
+ QString spec;
+ bool init_opath_already, init_already, no_io;
+ QHash<QString, bool> init_compiler_already;
+ QString build_args(const QString &outdir=QString());
+ void checkMultipleDefinition(const QString &, const QString &);
+
+ //internal caches
+ mutable QHash<QString, QMakeLocalFileName> depHeuristicsCache;
+ mutable QHash<QString, QStringList> dependsCache;
+ mutable QHash<ReplaceExtraCompilerCacheKey, QString> extraCompilerVariablesCache;
+
+protected:
+ QStringList createObjectList(const QStringList &sources);
+
+ //makefile style generator functions
+ void writeObj(QTextStream &, const QString &src);
+ void writeInstalls(QTextStream &t, const QString &installs, bool noBuild=false);
+ void writeHeader(QTextStream &t);
+ void writeSubDirs(QTextStream &t);
+ void writeMakeQmake(QTextStream &t);
+ void writeExtraVariables(QTextStream &t);
+ void writeExtraTargets(QTextStream &t);
+ void writeExtraCompilerTargets(QTextStream &t);
+ void writeExtraCompilerVariables(QTextStream &t);
+ virtual bool writeStubMakefile(QTextStream &t);
+ virtual bool writeMakefile(QTextStream &t);
+
+ QString pkgConfigPrefix() const;
+ QString pkgConfigFileName(bool fixify=true);
+ QString pkgConfigFixPath(QString) const;
+ void writePkgConfigFile(); // for pkg-config
+
+ //generating subtarget makefiles
+ struct SubTarget
+ {
+ QString name;
+ QString in_directory, out_directory;
+ QString profile, target, makefile;
+ QStringList depends;
+ };
+ enum SubTargetFlags {
+ SubTargetInstalls=0x01,
+ SubTargetOrdered=0x02,
+ SubTargetSkipDefaultVariables=0x04,
+ SubTargetSkipDefaultTargets=0x08,
+
+ SubTargetsNoFlags=0x00
+ };
+ QList<MakefileGenerator::SubTarget*> findSubDirsSubTargets() const;
+ void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags);
+
+ //extra compiler interface
+ bool verifyExtraCompiler(const QString &c, const QString &f);
+ virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &);
+ inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out)
+ { return replaceExtraCompilerVariables(val, QStringList(in), QStringList(out)); }
+
+ //interface to the source file info
+ QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool);
+ QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &);
+ QFileInfo findFileInfo(const QMakeLocalFileName &);
+ QMakeProject *project;
+
+ //escape
+ virtual QString unescapeFilePath(const QString &path) const;
+ virtual QStringList unescapeFilePaths(const QStringList &path) const;
+ virtual QString escapeFilePath(const QString &path) const { return path; }
+ virtual QString escapeDependencyPath(const QString &path) const { return escapeFilePath(path); }
+ QStringList escapeFilePaths(const QStringList &paths) const;
+ QStringList escapeDependencyPaths(const QStringList &paths) const;
+
+ //initialization
+ void verifyCompilers();
+ virtual void init();
+ void initOutPaths();
+ struct Compiler
+ {
+ QString variable_in;
+ enum CompilerFlag {
+ CompilerNoFlags = 0x00,
+ CompilerBuiltin = 0x01,
+ CompilerNoCheckDeps = 0x02,
+ CompilerRemoveNoExist = 0x04
+ };
+ uint flags, type;
+ };
+ void initCompiler(const Compiler &comp);
+ enum VPATHFlag {
+ VPATH_NoFlag = 0x00,
+ VPATH_WarnMissingFiles = 0x01,
+ VPATH_RemoveMissingFiles = 0x02,
+ VPATH_NoFixify = 0x04
+ };
+ QStringList findFilesInVPATH(QStringList l, uchar flags, const QString &var="");
+
+ inline int findExecutable(const QStringList &cmdline)
+ { int ret; canExecute(cmdline, &ret); return ret; }
+ bool canExecute(const QStringList &cmdline, int *argv0) const;
+ inline bool canExecute(const QString &cmdline) const
+ { return canExecute(cmdline.split(' '), 0); }
+
+ bool mkdir(const QString &dir) const;
+ QString mkdir_p_asstring(const QString &dir, bool escape=true) const;
+
+ //subclasses can use these to query information about how the generator was "run"
+ QString buildArgs(const QString &outdir=QString());
+ QString specdir(const QString &outdir=QString());
+
+ virtual QStringList &findDependencies(const QString &file);
+ virtual bool doDepends() const { return Option::mkfile::do_deps; }
+
+ void filterIncludedFiles(const QString &);
+ virtual void processSources() {
+ filterIncludedFiles("SOURCES");
+ filterIncludedFiles("GENERATED_SOURCES");
+ }
+
+ //for cross-platform dependent directories
+ virtual void usePlatformDir();
+
+ //for installs
+ virtual QString defaultInstall(const QString &);
+
+ //for prl
+ QString prlFileName(bool fixify=true);
+ void writePrlFile();
+ bool processPrlFile(QString &);
+ virtual void processPrlVariable(const QString &, const QStringList &);
+ virtual void processPrlFiles();
+ virtual void writePrlFile(QTextStream &);
+
+ //make sure libraries are found
+ virtual bool findLibraries();
+
+ //for retrieving values and lists of values
+ virtual QString var(const QString &var);
+ QString varGlue(const QString &var, const QString &before, const QString &glue, const QString &after);
+ QString varList(const QString &var);
+ QString val(const QStringList &varList);
+ QString valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after);
+ QString valList(const QStringList &varList);
+
+ QString filePrefixRoot(const QString &, const QString &);
+
+ //file fixification to unify all file names into a single pattern
+ enum FileFixifyType { FileFixifyAbsolute, FileFixifyRelative, FileFixifyDefault };
+ QString fileFixify(const QString& file, const QString &out_dir=QString(),
+ const QString &in_dir=QString(), FileFixifyType fix=FileFixifyDefault, bool canon=true) const;
+ inline QString fileFixify(const QString& file, FileFixifyType fix, bool canon=true) const
+ { return fileFixify(file, QString(), QString(), fix, canon); }
+ QStringList fileFixify(const QStringList& files, const QString &out_dir=QString(),
+ const QString &in_dir=QString(), FileFixifyType fix=FileFixifyDefault, bool canon=true) const;
+ inline QStringList fileFixify(const QStringList& files, FileFixifyType fix, bool canon=true) const
+ { return fileFixify(files, QString(), QString(), fix, canon); }
+
+public:
+ MakefileGenerator();
+ virtual ~MakefileGenerator();
+ QMakeProject *projectFile() const;
+ void setProjectFile(QMakeProject *p);
+
+ void setNoIO(bool o);
+ bool noIO() const;
+
+ inline bool exists(QString file) const { return fileInfo(file).exists(); }
+ QFileInfo fileInfo(QString file) const;
+
+ static MakefileGenerator *create(QMakeProject *);
+ virtual bool write();
+ virtual bool writeProjectMakefile();
+ virtual bool supportsMetaBuild() { return true; }
+ virtual bool supportsMergedBuilds() { return false; }
+ virtual bool mergeBuildProject(MakefileGenerator * /*other*/) { return false; }
+ virtual bool openOutput(QFile &, const QString &build) const;
+ virtual bool isWindowsShell() const { return Option::host_mode == Option::HOST_WIN_MODE; }
+ virtual bool isForSymbianSbsv2() const { return false; } // FIXME: killme - i'm ugly!
+
+ /* The next one is to avoid having SymbianCommonGenerator as a virtually
+ inherited class of this class. Instead it is without a base class
+ (avoiding the virtual inheritance problem), and is allowed to use
+ functions defined in here.
+
+ To illustrate:
+ +-------------------+
+ | MakefileGenerator |
+ +-------------------+
+ ^ ^
+ | |
+ | X <-- Avoid this inheritance
+ | |
+ +------------------------+ +------------------------+
+ | UnixMakefileGenerator | | SymbianCommonGenerator |
+ | or | | |
+ | NmakeMakefileGenerator | | |
+ +------------------------+ +------------------------+
+ ^ ^
+ | |
+ | |
+ | |
+ +-----------------------------+
+ | SymbianMakefileTemplate<> |
+ +-----------------------------+
+
+ We want to avoid the famous diamond problem, because if we have that, we need
+ virtual inheritance, which not all compilers like. Therefore, we break the
+ link as illustrated. Instead, we have a pointer to MakefileGenerator inside
+ SymbianCommonGenerator, and allows full access by making it a friend here.
+ */
+ friend class SymbianCommonGenerator;
+};
+
+inline void MakefileGenerator::setNoIO(bool o)
+{ no_io = o; }
+
+inline bool MakefileGenerator::noIO() const
+{ return no_io; }
+
+inline QString MakefileGenerator::defaultInstall(const QString &)
+{ return QString(""); }
+
+inline bool MakefileGenerator::findLibraries()
+{ return true; }
+
+inline MakefileGenerator::~MakefileGenerator()
+{ }
+
+QT_END_NAMESPACE
+
+#endif // MAKEFILE_H
diff --git a/qmake/generators/makefiledeps.cpp b/qmake/generators/makefiledeps.cpp
new file mode 100644
index 0000000000..56dc9917c0
--- /dev/null
+++ b/qmake/generators/makefiledeps.cpp
@@ -0,0 +1,961 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "makefiledeps.h"
+#include "option.h"
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qbuffer.h>
+#include <qplatformdefs.h>
+#if defined(Q_OS_UNIX)
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+#include <qdebug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <share.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if 1
+#define qmake_endOfLine(c) (c == '\r' || c == '\n')
+#else
+inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); }
+#endif
+
+//#define QMAKE_USE_CACHE
+
+QMakeLocalFileName::QMakeLocalFileName(const QString &name) : is_null(name.isNull())
+{
+ if(!name.isEmpty()) {
+ if(name.at(0) == QLatin1Char('"') && name.at(name.length()-2) == QLatin1Char('"'))
+ real_name = name.mid(1, name.length()-2);
+ else
+ real_name = name;
+ }
+}
+const QString
+&QMakeLocalFileName::local() const
+{
+ if(!is_null && local_name.isNull())
+ local_name = Option::fixPathToLocalOS(real_name, true);
+ return local_name;
+}
+
+struct SourceDependChildren;
+struct SourceFile {
+ SourceFile() : deps(0), type(QMakeSourceFileInfo::TYPE_UNKNOWN),
+ mocable(0), traversed(0), exists(1),
+ moc_checked(0), dep_checked(0), included_count(0) { }
+ ~SourceFile();
+ QMakeLocalFileName file;
+ SourceDependChildren *deps;
+ QMakeSourceFileInfo::SourceFileType type;
+ uint mocable : 1, traversed : 1, exists : 1;
+ uint moc_checked : 1, dep_checked : 1;
+ uchar included_count;
+};
+struct SourceDependChildren {
+ SourceFile **children;
+ int num_nodes, used_nodes;
+ SourceDependChildren() : children(0), num_nodes(0), used_nodes(0) { }
+ ~SourceDependChildren() { if(children) free(children); children = 0; }
+ void addChild(SourceFile *s) {
+ if(num_nodes <= used_nodes) {
+ num_nodes += 200;
+ children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes));
+ }
+ children[used_nodes++] = s;
+ }
+};
+SourceFile::~SourceFile() { delete deps; }
+class SourceFiles {
+ int hash(const char *);
+public:
+ SourceFiles();
+ ~SourceFiles();
+
+ SourceFile *lookupFile(const char *);
+ inline SourceFile *lookupFile(const QString &f) { return lookupFile(f.toLatin1().constData()); }
+ inline SourceFile *lookupFile(const QMakeLocalFileName &f) { return lookupFile(f.local().toLatin1().constData()); }
+ void addFile(SourceFile *, const char *k=0, bool own=true);
+
+ struct SourceFileNode {
+ SourceFileNode() : key(0), next(0), file(0), own_file(1) { }
+ ~SourceFileNode() {
+ delete [] key;
+ if(own_file)
+ delete file;
+ }
+ char *key;
+ SourceFileNode *next;
+ SourceFile *file;
+ uint own_file : 1;
+ } **nodes;
+ int num_nodes;
+};
+SourceFiles::SourceFiles()
+{
+ nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037));
+ for(int n = 0; n < num_nodes; n++)
+ nodes[n] = 0;
+}
+
+SourceFiles::~SourceFiles()
+{
+ for(int n = 0; n < num_nodes; n++) {
+ for(SourceFileNode *next = nodes[n]; next;) {
+ SourceFileNode *next_next = next->next;
+ delete next;
+ next = next_next;
+ }
+ }
+ free(nodes);
+}
+
+int SourceFiles::hash(const char *file)
+{
+ uint h = 0, g;
+ while (*file) {
+ h = (h << 4) + *file;
+ if ((g = (h & 0xf0000000)) != 0)
+ h ^= g >> 23;
+ h &= ~g;
+ file++;
+ }
+ return h;
+}
+
+SourceFile *SourceFiles::lookupFile(const char *file)
+{
+ int h = hash(file) % num_nodes;
+ for(SourceFileNode *p = nodes[h]; p; p = p->next) {
+ if(!strcmp(p->key, file))
+ return p->file;
+ }
+ return 0;
+}
+
+void SourceFiles::addFile(SourceFile *p, const char *k, bool own_file)
+{
+ QByteArray ba = p->file.local().toLatin1();
+ if(!k)
+ k = ba;
+ int h = hash(k) % num_nodes;
+ SourceFileNode *pn = new SourceFileNode;
+ pn->own_file = own_file;
+ pn->key = qstrdup(k);
+ pn->file = p;
+ pn->next = nodes[h];
+ nodes[h] = pn;
+}
+
+void QMakeSourceFileInfo::dependTreeWalker(SourceFile *node, SourceDependChildren *place)
+{
+ if(node->traversed || !node->exists)
+ return;
+ place->addChild(node);
+ node->traversed = true; //set flag
+ if(node->deps) {
+ for(int i = 0; i < node->deps->used_nodes; i++)
+ dependTreeWalker(node->deps->children[i], place);
+ }
+}
+
+void QMakeSourceFileInfo::setDependencyPaths(const QList<QMakeLocalFileName> &l)
+{
+ // Ensure that depdirs does not contain the same paths several times, to minimize the stats
+ QList<QMakeLocalFileName> ll;
+ for (int i = 0; i < l.count(); ++i) {
+ if (!ll.contains(l.at(i)))
+ ll.append(l.at(i));
+ }
+ depdirs = ll;
+}
+
+QStringList QMakeSourceFileInfo::dependencies(const QString &file)
+{
+ QStringList ret;
+ if(!files)
+ return ret;
+
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) {
+ if(node->deps) {
+ /* I stick them into a SourceDependChildren here because it is faster to just
+ iterate over the list to stick them in the list, and reset the flag, then it is
+ to loop over the tree (about 50% faster I saw) --Sam */
+ SourceDependChildren place;
+ for(int i = 0; i < node->deps->used_nodes; i++)
+ dependTreeWalker(node->deps->children[i], &place);
+ if(place.children) {
+ for(int i = 0; i < place.used_nodes; i++) {
+ place.children[i]->traversed = false; //reset flag
+ ret.append(place.children[i]->file.real());
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+int
+QMakeSourceFileInfo::included(const QString &file)
+{
+ if (!files)
+ return 0;
+
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file)))
+ return node->included_count;
+ return 0;
+}
+
+bool QMakeSourceFileInfo::mocable(const QString &file)
+{
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file)))
+ return node->mocable;
+ return false;
+}
+
+QMakeSourceFileInfo::QMakeSourceFileInfo(const QString &cf)
+{
+ //dep_mode
+ dep_mode = Recursive;
+
+ //quick project lookups
+ includes = files = 0;
+ files_changed = false;
+
+ //buffer
+ spare_buffer = 0;
+ spare_buffer_size = 0;
+
+ //cache
+ cachefile = cf;
+ if(!cachefile.isEmpty())
+ loadCache(cachefile);
+}
+
+QMakeSourceFileInfo::~QMakeSourceFileInfo()
+{
+ //cache
+ if(!cachefile.isEmpty() /*&& files_changed*/)
+ saveCache(cachefile);
+
+ //buffer
+ if(spare_buffer) {
+ free(spare_buffer);
+ spare_buffer = 0;
+ spare_buffer_size = 0;
+ }
+
+ //quick project lookup
+ delete files;
+ delete includes;
+}
+
+void QMakeSourceFileInfo::setCacheFile(const QString &cf)
+{
+ cachefile = cf;
+ loadCache(cachefile);
+}
+
+void QMakeSourceFileInfo::addSourceFiles(const QStringList &l, uchar seek,
+ QMakeSourceFileInfo::SourceFileType type)
+{
+ for(int i=0; i<l.size(); ++i)
+ addSourceFile(l.at(i), seek, type);
+}
+void QMakeSourceFileInfo::addSourceFile(const QString &f, uchar seek,
+ QMakeSourceFileInfo::SourceFileType type)
+{
+ if(!files)
+ files = new SourceFiles;
+
+ QMakeLocalFileName fn(f);
+ SourceFile *file = files->lookupFile(fn);
+ if(!file) {
+ file = new SourceFile;
+ file->file = fn;
+ files->addFile(file);
+ } else {
+ if(file->type != type && file->type != TYPE_UNKNOWN && type != TYPE_UNKNOWN)
+ warn_msg(WarnLogic, "%s is marked as %d, then %d!", f.toLatin1().constData(),
+ file->type, type);
+ }
+ if(type != TYPE_UNKNOWN)
+ file->type = type;
+
+ if(seek & SEEK_MOCS && !file->moc_checked)
+ findMocs(file);
+ if(seek & SEEK_DEPS && !file->dep_checked)
+ findDeps(file);
+}
+
+bool QMakeSourceFileInfo::containsSourceFile(const QString &f, SourceFileType type)
+{
+ if(SourceFile *file = files->lookupFile(QMakeLocalFileName(f)))
+ return (file->type == type || file->type == TYPE_UNKNOWN || type == TYPE_UNKNOWN);
+ return false;
+}
+
+char *QMakeSourceFileInfo::getBuffer(int s) {
+ if(!spare_buffer || spare_buffer_size < s)
+ spare_buffer = (char *)realloc(spare_buffer, spare_buffer_size=s);
+ return spare_buffer;
+}
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (x & _S_IFDIR)
+#endif
+
+QMakeLocalFileName QMakeSourceFileInfo::fixPathForFile(const QMakeLocalFileName &f, bool)
+{
+ return f;
+}
+
+QMakeLocalFileName QMakeSourceFileInfo::findFileForDep(const QMakeLocalFileName &/*dep*/,
+ const QMakeLocalFileName &/*file*/)
+{
+ return QMakeLocalFileName();
+}
+
+QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep)
+{
+ return QFileInfo(dep.real());
+}
+
+bool QMakeSourceFileInfo::findDeps(SourceFile *file)
+{
+ if(file->dep_checked || file->type == TYPE_UNKNOWN)
+ return true;
+ files_changed = true;
+ file->dep_checked = true;
+
+ const QMakeLocalFileName sourceFile = fixPathForFile(file->file, true);
+
+ struct stat fst;
+ char *buffer = 0;
+ int buffer_len = 0;
+ {
+ int fd;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_sopen_s(&fd, sourceFile.local().toLatin1().constData(),
+ _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
+ fd = -1;
+#else
+ fd = open(sourceFile.local().toLatin1().constData(), O_RDONLY);
+#endif
+ if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
+ return false;
+ buffer = getBuffer(fst.st_size);
+ for(int have_read = 0;
+ (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
+ buffer_len += have_read) ;
+ QT_CLOSE(fd);
+ }
+ if(!buffer)
+ return false;
+ if(!file->deps)
+ file->deps = new SourceDependChildren;
+
+ int line_count = 1;
+
+ for(int x = 0; x < buffer_len; ++x) {
+ bool try_local = true;
+ char *inc = 0;
+ if(file->type == QMakeSourceFileInfo::TYPE_UI) {
+ // skip whitespaces
+ while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'))
+ ++x;
+ if(*(buffer + x) == '<') {
+ ++x;
+ if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) &&
+ (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) {
+ for(x += 11; *(buffer + x) != '>'; ++x) ;
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) &&
+ (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) {
+ for(x += 13; *(buffer + x) != '>'; ++x) ; //skip up to >
+ while(x < buffer_len) {
+ for(x++; *(buffer + x) != '<'; ++x) ; //skip up to <
+ x++;
+ if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) &&
+ (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) {
+ for(x += 7; *(buffer + x) != '>'; ++x) ; //skip up to >
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ break;
+ } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) &&
+ (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) {
+ x += 14;
+ break;
+ }
+ }
+ } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) &&
+ (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) {
+ for(x += 8; *(buffer + x) != '>'; ++x) {
+ if(buffer_len >= x + 9 && *(buffer + x) == 'i' &&
+ !strncmp(buffer + x, "impldecl", 8)) {
+ for(x += 8; *(buffer + x) != '='; ++x) ;
+ if(*(buffer + x) != '=')
+ continue;
+ for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x) ;
+ char quote = 0;
+ if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ quote = *(buffer + x);
+ ++x;
+ }
+ int val_len;
+ for(val_len = 0; true; ++val_len) {
+ if(quote) {
+ if(*(buffer+x+val_len) == quote)
+ break;
+ } else if(*(buffer + x + val_len) == '>' ||
+ *(buffer + x + val_len) == ' ') {
+ break;
+ }
+ }
+//? char saved = *(buffer + x + val_len);
+ *(buffer + x + val_len) = '\0';
+ if(!strcmp(buffer+x, "in implementation")) {
+ //### do this
+ }
+ }
+ }
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ }
+ }
+ //read past new line now..
+ for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
+ ++line_count;
+ } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
+ } else if(file->type == QMakeSourceFileInfo::TYPE_C) {
+ for(int beginning=1; x < buffer_len; ++x) {
+ // whitespace comments and line-endings
+ for(; x < buffer_len; ++x) {
+ if(*(buffer+x) == ' ' || *(buffer+x) == '\t') {
+ // keep going
+ } else if(*(buffer+x) == '/') {
+ ++x;
+ if(buffer_len >= x) {
+ if(*(buffer+x) == '/') { //c++ style comment
+ for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
+ beginning = 1;
+ } else if(*(buffer+x) == '*') { //c style comment
+ for(++x; x < buffer_len; ++x) {
+ if(*(buffer+x) == '*') {
+ if(x < buffer_len-1 && *(buffer + (x+1)) == '/') {
+ ++x;
+ break;
+ }
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ }
+ }
+ }
+ }
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ beginning = 1;
+ } else {
+ break;
+ }
+ }
+
+ if(x >= buffer_len)
+ break;
+
+ // preprocessor directive
+ if(beginning && *(buffer+x) == '#')
+ break;
+
+ // quoted strings
+ if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ for(; x < buffer_len; ++x) {
+ if(*(buffer+x) == term) {
+ ++x;
+ break;
+ } else if(*(buffer+x) == '\\') {
+ ++x;
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ }
+ }
+ }
+ beginning = 0;
+ }
+ if(x >= buffer_len)
+ break;
+
+ //got a preprocessor symbol
+ ++x;
+ while(x < buffer_len) {
+ if(*(buffer+x) != ' ' && *(buffer+x) != '\t')
+ break;
+ ++x;
+ }
+
+ int keyword_len = 0;
+ const char *keyword = buffer+x;
+ while(x+keyword_len < buffer_len) {
+ if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) &&
+ *(buffer+x+keyword_len) != '_') {
+ for(x+=keyword_len; //skip spaces after keyword
+ x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t');
+ x++) ;
+ break;
+ } else if(qmake_endOfLine(*(buffer+x+keyword_len))) {
+ x += keyword_len-1;
+ keyword_len = 0;
+ break;
+ }
+ keyword_len++;
+ }
+
+ if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) {
+ char term = *(buffer + x);
+ if(term == '<') {
+ try_local = false;
+ term = '>';
+ } else if(term != '"') { //wtf?
+ continue;
+ }
+ x++;
+
+ int inc_len;
+ for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len) ;
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ x += inc_len;
+ } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) {
+ char term = 0;
+ if(*(buffer + x) == '"')
+ term = '"';
+ if(*(buffer + x) == '\'')
+ term = '\'';
+ if(term)
+ x++;
+
+ int msg_len;
+ for(msg_len = 0; (term && *(buffer + x + msg_len) != term) &&
+ !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len) ;
+ *(buffer + x + msg_len) = '\0';
+ debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x);
+ x += msg_len;
+ } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ while(x < buffer_len) {
+ if(*(buffer+x) == term)
+ break;
+ if(*(buffer+x) == '\\') {
+ x+=2;
+ } else {
+ if(qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ ++x;
+ }
+ }
+ } else {
+ --x;
+ }
+ }
+
+ if(inc) {
+ if(!includes)
+ includes = new SourceFiles;
+ SourceFile *dep = includes->lookupFile(inc);
+ if(!dep) {
+ bool exists = false;
+ QMakeLocalFileName lfn(inc);
+ if(QDir::isRelativePath(lfn.real())) {
+ if(try_local) {
+ QDir sourceDir = findFileInfo(sourceFile).dir();
+ QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local()));
+ if(findFileInfo(f).exists()) {
+ lfn = fixPathForFile(f);
+ exists = true;
+ }
+ }
+ if(!exists) { //path lookup
+ for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) {
+ QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real());
+ QFileInfo fi(findFileInfo(f));
+ if(fi.exists() && !fi.isDir()) {
+ lfn = fixPathForFile(f);
+ exists = true;
+ break;
+ }
+ }
+ }
+ if(!exists) { //heuristic lookup
+ lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
+ if((exists = !lfn.isNull()))
+ lfn = fixPathForFile(lfn);
+ }
+ } else {
+ exists = QFile::exists(lfn.real());
+ }
+ if(!lfn.isNull()) {
+ dep = files->lookupFile(lfn);
+ if(!dep) {
+ dep = new SourceFile;
+ dep->file = lfn;
+ dep->type = QMakeSourceFileInfo::TYPE_C;
+ files->addFile(dep);
+ includes->addFile(dep, inc, false);
+ }
+ dep->exists = exists;
+ }
+ }
+ if(dep && dep->file != file->file) {
+ dep->included_count++;
+ if(dep->exists) {
+ debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
+ line_count, dep->file.local().toLatin1().constData());
+ file->deps->addChild(dep);
+ }
+ }
+ }
+ }
+ if(dependencyMode() == Recursive) { //done last because buffer is shared
+ for(int i = 0; i < file->deps->used_nodes; i++) {
+ if(!file->deps->children[i]->deps)
+ findDeps(file->deps->children[i]);
+ }
+ }
+ return true;
+}
+
+bool QMakeSourceFileInfo::findMocs(SourceFile *file)
+{
+ if(file->moc_checked)
+ return true;
+ files_changed = true;
+ file->moc_checked = true;
+
+ int buffer_len;
+ char *buffer = 0;
+ {
+ struct stat fst;
+ int fd;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
+ _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0)
+ fd = -1;
+#else
+ fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
+#endif
+ if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
+ return false; //shouldn't happen
+ buffer = getBuffer(fst.st_size);
+ for(int have_read = buffer_len = 0;
+ (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
+ buffer_len += have_read) ;
+ QT_CLOSE(fd);
+ }
+
+ debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
+ int line_count = 1;
+ bool ignore_qobject = false, ignore_qgadget = false;
+ /* qmake ignore Q_GADGET */
+ /* qmake ignore Q_OBJECT */
+ for(int x = 0; x < buffer_len; x++) {
+ if(*(buffer + x) == '/') {
+ ++x;
+ if(buffer_len >= x) {
+ if(*(buffer + x) == '/') { //c++ style comment
+ for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
+ } else if(*(buffer + x) == '*') { //c style comment
+ for(++x; x < buffer_len; ++x) {
+ if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore
+ if(buffer_len >= (x + 20) &&
+ !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) {
+ debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
+ file->file.real().toLatin1().constData(), line_count);
+ x += 20;
+ ignore_qobject = true;
+ } else if(buffer_len >= (x + 20) &&
+ !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) {
+ debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"",
+ file->file.real().toLatin1().constData(), line_count);
+ x += 20;
+ ignore_qgadget = true;
+ }
+ } else if(*(buffer + x) == '*') {
+ if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') {
+ ++x;
+ break;
+ }
+ } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) {
+ ++line_count;
+ }
+ }
+ }
+ }
+ } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ while(x < buffer_len) {
+ if(*(buffer+x) == term)
+ break;
+ if(*(buffer+x) == '\\') {
+ x+=2;
+ } else {
+ if(qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ ++x;
+ }
+ }
+ }
+ if(Option::debug_level && qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ if(((buffer_len > x+2 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_')
+ ||
+ (buffer_len > x+4 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O'
+ && *(buffer+x+3) == 'M' && *(buffer+x+4) == '_'))
+ &&
+ *(buffer + x) != '_' &&
+ (*(buffer + x) < 'a' || *(buffer + x) > 'z') &&
+ (*(buffer + x) < 'A' || *(buffer + x) > 'Z') &&
+ (*(buffer + x) < '0' || *(buffer + x) > '9')) {
+ ++x;
+ int match = 0;
+ static const char *interesting[] = { "OBJECT", "GADGET",
+ "M_OBJECT" };
+ for(int interest = 0, m1, m2; interest < 3; ++interest) {
+ if(interest == 0 && ignore_qobject)
+ continue;
+ else if(interest == 1 && ignore_qgadget)
+ continue;
+ for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) {
+ if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) {
+ m2 = -1;
+ break;
+ }
+ ++m2;
+ }
+ if(m1 == m2) {
+ match = m2 + 2;
+ break;
+ }
+ }
+ if(match && *(buffer+x+match) != '_' &&
+ (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') &&
+ (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') &&
+ (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) {
+ if(Option::debug_level) {
+ *(buffer+x+match) = '\0';
+ debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(),
+ line_count, buffer+x);
+ }
+ file->mocable = true;
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+
+void QMakeSourceFileInfo::saveCache(const QString &cf)
+{
+#ifdef QMAKE_USE_CACHE
+ if(cf.isEmpty())
+ return;
+
+ QFile file(QMakeLocalFileName(cf).local());
+ if(file.open(QIODevice::WriteOnly)) {
+ QTextStream stream(&file);
+ stream << qmake_version() << endl << endl; //version
+ { //cache verification
+ QMap<QString, QStringList> verify = getCacheVerification();
+ stream << verify.count() << endl;
+ for(QMap<QString, QStringList>::iterator it = verify.begin();
+ it != verify.end(); ++it) {
+ stream << it.key() << endl << it.value().join(";") << endl;
+ }
+ stream << endl;
+ }
+ if(files->nodes) {
+ for(int file = 0; file < files->num_nodes; ++file) {
+ for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) {
+ stream << node->file->file.local() << endl; //source
+ stream << node->file->type << endl; //type
+
+ //depends
+ stream << ";";
+ if(node->file->deps) {
+ for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) {
+ if(depend)
+ stream << ";";
+ stream << node->file->deps->children[depend]->file.local();
+ }
+ }
+ stream << endl;
+
+ stream << node->file->mocable << endl; //mocable
+ stream << endl; //just for human readability
+ }
+ }
+ }
+ stream.flush();
+ file.close();
+ }
+#else
+ Q_UNUSED(cf);
+#endif
+}
+
+void QMakeSourceFileInfo::loadCache(const QString &cf)
+{
+ if(cf.isEmpty())
+ return;
+
+#ifdef QMAKE_USE_CACHE
+ QMakeLocalFileName cache_file(cf);
+ int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY);
+ if(fd == -1)
+ return;
+ QFileInfo cache_fi = findFileInfo(cache_file);
+ if(!cache_fi.exists() || cache_fi.isDir())
+ return;
+
+ QFile file;
+ if(!file.open(QIODevice::ReadOnly, fd))
+ return;
+ QTextStream stream(&file);
+
+ if(stream.readLine() == qmake_version()) { //version check
+ stream.skipWhiteSpace();
+
+ bool verified = true;
+ { //cache verification
+ QMap<QString, QStringList> verify;
+ int len = stream.readLine().toInt();
+ for(int i = 0; i < len; ++i) {
+ QString var = stream.readLine();
+ QString val = stream.readLine();
+ verify.insert(var, val.split(';', QString::SkipEmptyParts));
+ }
+ verified = verifyCache(verify);
+ }
+ if(verified) {
+ stream.skipWhiteSpace();
+ if(!files)
+ files = new SourceFiles;
+ while(!stream.atEnd()) {
+ QString source = stream.readLine();
+ QString type = stream.readLine();
+ QString depends = stream.readLine();
+ QString mocable = stream.readLine();
+ stream.skipWhiteSpace();
+
+ QMakeLocalFileName fn(source);
+ QFileInfo fi = findFileInfo(fn);
+
+ SourceFile *file = files->lookupFile(fn);
+ if(!file) {
+ file = new SourceFile;
+ file->file = fn;
+ files->addFile(file);
+ file->type = (SourceFileType)type.toInt();
+ file->exists = fi.exists();
+ }
+ if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) {
+ if(!file->dep_checked) { //get depends
+ if(!file->deps)
+ file->deps = new SourceDependChildren;
+ file->dep_checked = true;
+ QStringList depend_list = depends.split(";", QString::SkipEmptyParts);
+ for(int depend = 0; depend < depend_list.size(); ++depend) {
+ QMakeLocalFileName dep_fn(depend_list.at(depend));
+ QFileInfo dep_fi(findFileInfo(dep_fn));
+ SourceFile *dep = files->lookupFile(dep_fn);
+ if(!dep) {
+ dep = new SourceFile;
+ dep->file = dep_fn;
+ dep->exists = dep_fi.exists();
+ dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN;
+ files->addFile(dep);
+ }
+ dep->included_count++;
+ file->deps->addChild(dep);
+ }
+ }
+ if(!file->moc_checked) { //get mocs
+ file->moc_checked = true;
+ file->mocable = mocable.toInt();
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
+{
+ return QMap<QString, QStringList>();
+}
+
+bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
+{
+ return v == getCacheVerification();
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/makefiledeps.h b/qmake/generators/makefiledeps.h
new file mode 100644
index 0000000000..c3f8770797
--- /dev/null
+++ b/qmake/generators/makefiledeps.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAKEFILEDEPS_H
+#define MAKEFILEDEPS_H
+
+#include <qstringlist.h>
+#include <qfileinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+struct SourceFile;
+struct SourceDependChildren;
+class SourceFiles;
+
+class QMakeLocalFileName {
+ uint is_null : 1;
+ mutable QString real_name, local_name;
+public:
+ QMakeLocalFileName() : is_null(1) { }
+ QMakeLocalFileName(const QString &);
+ bool isNull() const { return is_null; }
+ inline const QString &real() const { return real_name; }
+ const QString &local() const;
+
+ bool operator==(const QMakeLocalFileName &other) {
+ return (this->real_name == other.real_name);
+ }
+ bool operator!=(const QMakeLocalFileName &other) {
+ return !(*this == other);
+ }
+};
+
+class QMakeSourceFileInfo
+{
+private:
+ //quick project lookups
+ SourceFiles *files, *includes;
+ bool files_changed;
+ QList<QMakeLocalFileName> depdirs;
+
+ //sleezy buffer code
+ char *spare_buffer;
+ int spare_buffer_size;
+ char *getBuffer(int s);
+
+ //actual guts
+ bool findMocs(SourceFile *);
+ bool findDeps(SourceFile *);
+ void dependTreeWalker(SourceFile *, SourceDependChildren *);
+
+ //cache
+ QString cachefile;
+
+protected:
+ virtual QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool forOpen=false);
+ virtual QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &);
+ virtual QFileInfo findFileInfo(const QMakeLocalFileName &);
+
+public:
+ QMakeSourceFileInfo(const QString &cachefile="");
+ virtual ~QMakeSourceFileInfo();
+
+ QList<QMakeLocalFileName> dependencyPaths() const { return depdirs; }
+ void setDependencyPaths(const QList<QMakeLocalFileName> &);
+
+ enum DependencyMode { Recursive, NonRecursive };
+ inline void setDependencyMode(DependencyMode mode) { dep_mode = mode; }
+ inline DependencyMode dependencyMode() const { return dep_mode; }
+
+ enum SourceFileType { TYPE_UNKNOWN, TYPE_C, TYPE_UI, TYPE_QRC };
+ enum SourceFileSeek { SEEK_DEPS=0x01, SEEK_MOCS=0x02 };
+ void addSourceFiles(const QStringList &, uchar seek, SourceFileType type=TYPE_C);
+ void addSourceFile(const QString &, uchar seek, SourceFileType type=TYPE_C);
+ bool containsSourceFile(const QString &, SourceFileType type=TYPE_C);
+
+ int included(const QString &file);
+ QStringList dependencies(const QString &file);
+
+ bool mocable(const QString &file);
+
+ virtual QMap<QString, QStringList> getCacheVerification();
+ virtual bool verifyCache(const QMap<QString, QStringList> &);
+ void setCacheFile(const QString &cachefile); //auto caching
+ void loadCache(const QString &cf);
+ void saveCache(const QString &cf);
+
+private:
+ DependencyMode dep_mode;
+};
+
+QT_END_NAMESPACE
+
+#endif // MAKEFILEDEPS_H
diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp
new file mode 100644
index 0000000000..a3fba6ab55
--- /dev/null
+++ b/qmake/generators/metamakefile.cpp
@@ -0,0 +1,572 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "metamakefile.h"
+#include "qregexp.h"
+#include "qdir.h"
+#include "qdebug.h"
+#include "makefile.h"
+#include "project.h"
+#include "cachekeys.h"
+
+#define BUILDSMETATYPE 1
+#define SUBDIRSMETATYPE 2
+
+QT_BEGIN_NAMESPACE
+
+MetaMakefileGenerator::~MetaMakefileGenerator()
+{
+ if(own_project)
+ delete project;
+}
+
+#ifndef QT_QMAKE_PARSER_ONLY
+
+class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
+{
+private:
+ bool init_flag;
+ struct Build {
+ QString name, build;
+ MakefileGenerator *makefile;
+ };
+ QList<Build *> makefiles;
+ void clearBuilds();
+ MakefileGenerator *processBuild(const QString &);
+
+public:
+
+ BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
+ virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); }
+
+ virtual bool init();
+ virtual int type() const { return BUILDSMETATYPE; }
+ virtual bool write(const QString &);
+};
+
+void
+BuildsMetaMakefileGenerator::clearBuilds()
+{
+ for(int i = 0; i < makefiles.count(); i++) {
+ Build *build = makefiles[i];
+ if(QMakeProject *p = build->makefile->projectFile()) {
+ if(p != project)
+ delete p;
+ }
+ delete build->makefile;
+ delete build;
+ }
+ makefiles.clear();
+}
+
+bool
+BuildsMetaMakefileGenerator::init()
+{
+ if(init_flag)
+ return false;
+ init_flag = true;
+
+ const QStringList &builds = project->variables()["BUILDS"];
+ bool use_single_build = builds.isEmpty();
+ if(builds.count() > 1 && Option::output.fileName() == "-") {
+ use_single_build = true;
+ warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
+ } else if(0 && !use_single_build && project->first("TEMPLATE") == "subdirs") {
+ use_single_build = true;
+ warn_msg(WarnLogic, "Cannot specify multiple builds with TEMPLATE subdirs.");
+ }
+ if(!use_single_build) {
+ for(int i = 0; i < builds.count(); i++) {
+ QString build = builds[i];
+ MakefileGenerator *makefile = processBuild(build);
+ if(!makefile)
+ return false;
+ if(!makefile->supportsMetaBuild()) {
+ warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
+ clearBuilds();
+ use_single_build = true;
+ break;
+ } else {
+ Build *b = new Build;
+ b->name = name;
+ if(builds.count() != 1)
+ b->build += build;
+ b->makefile = makefile;
+ makefiles += b;
+ }
+ }
+ }
+ if(use_single_build) {
+ Build *build = new Build;
+ build->name = name;
+ build->makefile = createMakefileGenerator(project, false);
+ if (build->makefile){
+ makefiles += build;
+ }else {
+ delete build;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+BuildsMetaMakefileGenerator::write(const QString &oldpwd)
+{
+ Build *glue = 0;
+ if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) {
+ glue = new Build;
+ glue->name = name;
+ glue->makefile = createMakefileGenerator(project, true);
+ makefiles += glue;
+ }
+
+ bool ret = true;
+ const QString &output_name = Option::output.fileName();
+ for(int i = 0; ret && i < makefiles.count(); i++) {
+ Option::output.setFileName(output_name);
+ Build *build = makefiles[i];
+
+ bool using_stdout = false;
+ if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
+ Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
+ && (!build->makefile->supportsMergedBuilds()
+ || (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
+ //open output
+ if(!(Option::output.isOpen())) {
+ if(Option::output.fileName() == "-") {
+ Option::output.setFileName("");
+ Option::output_dir = qmake_getpwd();
+ Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
+ using_stdout = true;
+ } else {
+ if(Option::output.fileName().isEmpty() &&
+ Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
+ Option::output.setFileName(project->first("QMAKE_MAKEFILE"));
+ Option::output_dir = oldpwd;
+ QString build_name = build->name;
+ if(!build->build.isEmpty()) {
+ if(!build_name.isEmpty())
+ build_name += ".";
+ build_name += build->build;
+ }
+ if(!build->makefile->openOutput(Option::output, build_name)) {
+ fprintf(stderr, "Failure to open file: %s\n",
+ Option::output.fileName().isEmpty() ? "(stdout)" :
+ Option::output.fileName().toLatin1().constData());
+ return false;
+ }
+ }
+ }
+ } else {
+ using_stdout = true; //kind of..
+ }
+
+ if(!build->makefile) {
+ ret = false;
+ } else if(build == glue) {
+ ret = build->makefile->writeProjectMakefile();
+ } else {
+ ret = build->makefile->write();
+ if (glue && glue->makefile->supportsMergedBuilds())
+ ret = glue->makefile->mergeBuildProject(build->makefile);
+ }
+ if(!using_stdout) {
+ Option::output.close();
+ if(!ret)
+ Option::output.remove();
+ }
+
+ // debugging
+ if(Option::debug_level) {
+ debug_msg(1, "Dumping all variables:");
+ QMap<QString, QStringList> &vars = project->variables();
+ for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
+ if(!it.key().startsWith(".") && !it.value().isEmpty())
+ debug_msg(1, "%s === %s", it.key().toLatin1().constData(),
+ it.value().join(" :: ").toLatin1().constData());
+ }
+ }
+ }
+ return ret;
+}
+
+MakefileGenerator
+*BuildsMetaMakefileGenerator::processBuild(const QString &build)
+{
+ if(project) {
+ debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
+ project->projectFile().toLatin1().constData(),build.toLatin1().constData());
+
+ //initialize the base
+ QMap<QString, QStringList> basevars;
+ if(!project->isEmpty(build + ".CONFIG"))
+ basevars["CONFIG"] += project->values(build + ".CONFIG");
+ basevars["CONFIG"] += build;
+ basevars["CONFIG"] += "build_pass";
+ basevars["BUILD_PASS"] = QStringList(build);
+ QStringList buildname = project->values(build + ".name");
+ basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname);
+
+ //create project
+ QMakeProject *build_proj = new QMakeProject(project->properties(), basevars);
+
+ //all the user configs must be set again afterwards (for .pro tests and for .prf tests)
+ const QStringList old_after_user_config = Option::after_user_configs;
+ const QStringList old_user_config = Option::user_configs;
+ Option::after_user_configs += basevars["CONFIG"];
+ Option::user_configs += basevars["CONFIG"];
+ build_proj->read(project->projectFile());
+ Option::after_user_configs = old_after_user_config;
+ Option::user_configs = old_user_config;
+
+ //done
+ return createMakefileGenerator(build_proj);
+ }
+ return 0;
+}
+
+class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
+{
+protected:
+ bool init_flag;
+ struct Subdir {
+ Subdir() : makefile(0), indent(0) { }
+ ~Subdir() { delete makefile; }
+ QString input_dir;
+ QString output_dir, output_file;
+ MetaMakefileGenerator *makefile;
+ int indent;
+ };
+ QList<Subdir *> subs;
+ MakefileGenerator *processBuild(const QString &);
+
+public:
+ SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
+ virtual ~SubdirsMetaMakefileGenerator();
+
+ virtual bool init();
+ virtual int type() const { return SUBDIRSMETATYPE; }
+ virtual bool write(const QString &);
+};
+
+bool
+SubdirsMetaMakefileGenerator::init()
+{
+ if(init_flag)
+ return false;
+ init_flag = true;
+ bool hasError = false;
+
+ // It might make sense to bequeath the CONFIG option to the recursed
+ // projects. OTOH, one would most likely have it in all projects anyway -
+ // either through a qmakespec, a .qmake.cache or explicitly - as otherwise
+ // running qmake in a subdirectory would have a different auto-recurse
+ // setting than in parent directories.
+ bool recurse = Option::recursive == Option::QMAKE_RECURSIVE_YES
+ || (Option::recursive == Option::QMAKE_RECURSIVE_DEFAULT
+ && project->isRecursive());
+ if(recurse) {
+ QString old_output_dir = Option::output_dir;
+ QString old_output = Option::output.fileName();
+ QString oldpwd = qmake_getpwd();
+ QString thispwd = oldpwd;
+ if(!thispwd.endsWith('/'))
+ thispwd += '/';
+ const QStringList &subdirs = project->values("SUBDIRS");
+ static int recurseDepth = -1;
+ ++recurseDepth;
+ for(int i = 0; i < subdirs.size(); ++i) {
+ Subdir *sub = new Subdir;
+ sub->indent = recurseDepth;
+
+ QFileInfo subdir(subdirs.at(i));
+ if(!project->isEmpty(subdirs.at(i) + ".file"))
+ subdir = project->first(subdirs.at(i) + ".file");
+ else if(!project->isEmpty(subdirs.at(i) + ".subdir"))
+ subdir = project->first(subdirs.at(i) + ".subdir");
+ QString sub_name;
+ if(subdir.isDir())
+ subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
+ else
+ sub_name = subdir.baseName();
+ if(!subdir.isRelative()) { //we can try to make it relative
+ QString subdir_path = subdir.filePath();
+ if(subdir_path.startsWith(thispwd))
+ subdir = QFileInfo(subdir_path.mid(thispwd.length()));
+ }
+
+ //handle sub project
+ QMakeProject *sub_proj = new QMakeProject(project->properties());
+ for (int ind = 0; ind < sub->indent; ++ind)
+ printf(" ");
+ sub->input_dir = subdir.absolutePath();
+ if(subdir.isRelative() && old_output_dir != oldpwd) {
+ sub->output_dir = old_output_dir + "/" + subdir.path();
+ printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
+ } else { //what about shadow builds?
+ sub->output_dir = sub->input_dir;
+ printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
+ }
+ qmake_setpwd(sub->input_dir);
+ Option::output_dir = sub->output_dir;
+ bool tmpError = !sub_proj->read(subdir.fileName());
+ if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
+ fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
+ subdir.fileName().toLatin1().constData(),
+ sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
+ delete sub;
+ delete sub_proj;
+ Option::output_dir = old_output_dir;
+ qmake_setpwd(oldpwd);
+ continue;
+ } else {
+ hasError |= tmpError;
+ }
+ sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
+ if(0 && sub->makefile->type() == SUBDIRSMETATYPE) {
+ subs.append(sub);
+ } else {
+ const QString output_name = Option::output.fileName();
+ Option::output.setFileName(sub->output_file);
+ hasError |= !sub->makefile->write(sub->output_dir);
+ delete sub;
+ qmakeClearCaches();
+ sub = 0;
+ Option::output.setFileName(output_name);
+ }
+ Option::output_dir = old_output_dir;
+ qmake_setpwd(oldpwd);
+
+ }
+ --recurseDepth;
+ Option::output.setFileName(old_output);
+ Option::output_dir = old_output_dir;
+ qmake_setpwd(oldpwd);
+ }
+
+ Subdir *self = new Subdir;
+ self->input_dir = qmake_getpwd();
+ self->output_dir = Option::output_dir;
+ if(!recurse || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
+ self->output_file = Option::output.fileName();
+ self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
+ self->makefile->init();
+ subs.append(self);
+
+ return !hasError;
+}
+
+bool
+SubdirsMetaMakefileGenerator::write(const QString &oldpwd)
+{
+ bool ret = true;
+ const QString &pwd = qmake_getpwd();
+ const QString &output_dir = Option::output_dir;
+ const QString &output_name = Option::output.fileName();
+ for(int i = 0; ret && i < subs.count(); i++) {
+ const Subdir *sub = subs.at(i);
+ qmake_setpwd(subs.at(i)->input_dir);
+ Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath();
+ if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/'))
+ Option::output_dir += QLatin1Char('/');
+ Option::output.setFileName(subs.at(i)->output_file);
+ if(i != subs.count()-1) {
+ for (int ind = 0; ind < sub->indent; ++ind)
+ printf(" ");
+ printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
+ Option::output.fileName()).toLatin1().constData());
+ }
+ QString writepwd = Option::fixPathToLocalOS(qmake_getpwd());
+ if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd)))
+ writepwd = oldpwd;
+ if(!(ret = subs.at(i)->makefile->write(writepwd)))
+ break;
+ //restore because I'm paranoid
+ qmake_setpwd(pwd);
+ Option::output.setFileName(output_name);
+ Option::output_dir = output_dir;
+ }
+ return ret;
+}
+
+SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
+{
+ for(int i = 0; i < subs.count(); i++)
+ delete subs[i];
+ subs.clear();
+}
+
+//Factory things
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "unixmake.h"
+#include "mingw_make.h"
+#include "projectgenerator.h"
+#include "pbuilder_pbx.h"
+#include "msvc_nmake.h"
+#include "borland_bmake.h"
+#include "msvc_vcproj.h"
+#include "msvc_vcxproj.h"
+#include "symmake_abld.h"
+#include "symmake_sbsv2.h"
+#include "symbian_makefile.h"
+#include "gbuild.h"
+QT_END_INCLUDE_NAMESPACE
+
+MakefileGenerator *
+MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
+{
+ MakefileGenerator *mkfile = NULL;
+ if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
+ mkfile = new ProjectGenerator;
+ mkfile->setProjectFile(proj);
+ return mkfile;
+ }
+
+ QString gen = proj->first("MAKEFILE_GENERATOR");
+ if(gen.isEmpty()) {
+ fprintf(stderr, "MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \"include(..)\" - enable qmake debugging to investigate more.\n",
+ proj->projectFile().toLatin1().constData());
+ } else if(gen == "UNIX") {
+ mkfile = new UnixMakefileGenerator;
+ } else if(gen == "MINGW") {
+ mkfile = new MingwMakefileGenerator;
+ } else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
+ mkfile = new ProjectBuilderMakefileGenerator;
+ } else if(gen == "MSVC.NET") {
+ if (proj->first("TEMPLATE").startsWith("vc"))
+ mkfile = new VcprojGenerator;
+ else
+ mkfile = new NmakeMakefileGenerator;
+ } else if(gen == "MSBUILD") {
+ // Visual Studio >= v11.0
+ if (proj->first("TEMPLATE").startsWith("vc"))
+ mkfile = new VcxprojGenerator;
+ else
+ mkfile = new NmakeMakefileGenerator;
+ } else if(gen == "BMAKE") {
+ mkfile = new BorlandMakefileGenerator;
+ } else if(gen == "SYMBIAN_ABLD") {
+ mkfile = new SymbianAbldMakefileGenerator;
+ } else if(gen == "SYMBIAN_SBSV2") {
+ mkfile = new SymbianSbsv2MakefileGenerator;
+ } else if(gen == "SYMBIAN_UNIX") {
+ mkfile = new SymbianMakefileTemplate<UnixMakefileGenerator>;
+ } else if(gen == "SYMBIAN_MINGW") {
+ mkfile = new SymbianMakefileTemplate<MingwMakefileGenerator>;
+ } else if(gen == "GBUILD") {
+ mkfile = new GBuildMakefileGenerator;
+ } else {
+ fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
+ }
+ if (mkfile) {
+ mkfile->setNoIO(noIO);
+ mkfile->setProjectFile(proj);
+ }
+ return mkfile;
+}
+
+MetaMakefileGenerator *
+MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success)
+{
+ MetaMakefileGenerator *ret = 0;
+ if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
+ Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
+ if (proj->first("TEMPLATE").endsWith("subdirs"))
+ ret = new SubdirsMetaMakefileGenerator(proj, name, op);
+ }
+ if (!ret)
+ ret = new BuildsMetaMakefileGenerator(proj, name, op);
+ bool res = ret->init();
+ if (success)
+ *success = res;
+ return ret;
+}
+
+#endif // QT_QMAKE_PARSER_ONLY
+
+bool
+MetaMakefileGenerator::modesForGenerator(const QString &gen,
+ Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode)
+{
+ if (gen == "UNIX") {
+#ifdef Q_OS_MAC
+ *host_mode = Option::HOST_MACX_MODE;
+ *target_mode = Option::TARG_MACX_MODE;
+#else
+ *host_mode = Option::HOST_UNIX_MODE;
+ *target_mode = Option::TARG_UNIX_MODE;
+#endif
+ } else if (gen == "MSVC.NET" || gen == "BMAKE" || gen == "MSBUILD") {
+ *host_mode = Option::HOST_WIN_MODE;
+ *target_mode = Option::TARG_WIN_MODE;
+ } else if (gen == "MINGW") {
+#if defined(Q_OS_MAC)
+ *host_mode = Option::HOST_MACX_MODE;
+#elif defined(Q_OS_UNIX)
+ *host_mode = Option::HOST_UNIX_MODE;
+#else
+ *host_mode = Option::HOST_WIN_MODE;
+#endif
+ *target_mode = Option::TARG_WIN_MODE;
+ } else if (gen == "PROJECTBUILDER" || gen == "XCODE") {
+ *host_mode = Option::HOST_MACX_MODE;
+ *target_mode = Option::TARG_MACX_MODE;
+ } else if (gen == "SYMBIAN_ABLD" || gen == "SYMBIAN_SBSV2" || gen == "SYMBIAN_UNIX" || gen == "SYMBIAN_MINGW") {
+#if defined(Q_OS_MAC)
+ *host_mode = Option::HOST_MACX_MODE;
+#elif defined(Q_OS_UNIX)
+ *host_mode = Option::HOST_UNIX_MODE;
+#else
+ *host_mode = Option::HOST_WIN_MODE;
+#endif
+ *target_mode = Option::TARG_SYMBIAN_MODE;
+ } else if (gen == "GBUILD") {
+ *host_mode = Option::HOST_UNIX_MODE;
+ *target_mode = Option::TARG_INTEGRITY_MODE;
+ } else {
+ fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/metamakefile.h b/qmake/generators/metamakefile.h
new file mode 100644
index 0000000000..c8592d3afc
--- /dev/null
+++ b/qmake/generators/metamakefile.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METAMAKEFILE_H
+#define METAMAKEFILE_H
+
+#include <option.h>
+
+#include <qlist.h>
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMakeProject;
+class MakefileGenerator;
+
+class MetaMakefileGenerator
+{
+protected:
+ MetaMakefileGenerator(QMakeProject *p, const QString &n, bool op=true) : project(p), own_project(op), name(n) { }
+ QMakeProject *project;
+ bool own_project;
+ QString name;
+
+public:
+
+ virtual ~MetaMakefileGenerator();
+
+ static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true, bool *success = 0);
+ static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false);
+
+ static bool modesForGenerator(const QString &generator,
+ Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode);
+
+ inline QMakeProject *projectFile() const { return project; }
+
+ virtual bool init() = 0;
+ virtual int type() const { return -1; }
+ virtual bool write(const QString &oldpwd) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // METAMAKEFILE_H
diff --git a/qmake/generators/projectgenerator.cpp b/qmake/generators/projectgenerator.cpp
new file mode 100644
index 0000000000..da634a1179
--- /dev/null
+++ b/qmake/generators/projectgenerator.cpp
@@ -0,0 +1,511 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "projectgenerator.h"
+#include "option.h"
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+
+QT_BEGIN_NAMESPACE
+
+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;
+}
+
+ProjectGenerator::ProjectGenerator() : MakefileGenerator(), init_flag(false)
+{
+}
+
+void
+ProjectGenerator::init()
+{
+ if(init_flag)
+ return;
+ int file_count = 0;
+ init_flag = true;
+ verifyCompilers();
+
+ project->read(QMakeProject::ReadFeatures);
+ project->variables()["CONFIG"].clear();
+
+ QMap<QString, QStringList> &v = project->variables();
+ QString templ = Option::user_template.isEmpty() ? QString("app") : Option::user_template;
+ if(!Option::user_template_prefix.isEmpty())
+ templ.prepend(Option::user_template_prefix);
+ v["TEMPLATE_ASSIGN"] += templ;
+
+ //figure out target
+ if(Option::output.fileName() == "-")
+ v["TARGET_ASSIGN"] = QStringList("unknown");
+ else
+ v["TARGET_ASSIGN"] = QStringList(QFileInfo(Option::output).baseName());
+
+ //the scary stuff
+ if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
+ QString builtin_regex = project_builtin_regx();
+ QStringList dirs = Option::projfile::project_dirs;
+ if(Option::projfile::do_pwd) {
+ if(!v["INCLUDEPATH"].contains("."))
+ v["INCLUDEPATH"] += ".";
+ dirs.prepend(qmake_getpwd());
+ }
+
+ for(int i = 0; i < dirs.count(); ++i) {
+ QString dir, regex, pd = dirs.at(i);
+ bool add_depend = false;
+ if(exists(pd)) {
+ QFileInfo fi(fileInfo(pd));
+ if(fi.isDir()) {
+ dir = pd;
+ add_depend = true;
+ if(dir.right(1) != Option::dir_sep)
+ dir += Option::dir_sep;
+ if(Option::recursive == Option::QMAKE_RECURSIVE_YES) {
+ QStringList files = QDir(dir).entryList(QDir::Files);
+ for(int i = 0; i < (int)files.count(); i++) {
+ if(files[i] != "." && files[i] != "..")
+ dirs.append(dir + files[i] + QDir::separator() + builtin_regex);
+ }
+ }
+ regex = builtin_regex;
+ } else {
+ QString file = pd;
+ int s = file.lastIndexOf(Option::dir_sep);
+ if(s != -1)
+ dir = file.left(s+1);
+ if(addFile(file)) {
+ add_depend = true;
+ file_count++;
+ }
+ }
+ } else { //regexp
+ regex = pd;
+ }
+ if(!regex.isEmpty()) {
+ int s = regex.lastIndexOf(Option::dir_sep);
+ if(s != -1) {
+ dir = regex.left(s+1);
+ regex = regex.right(regex.length() - (s+1));
+ }
+ if(Option::recursive == Option::QMAKE_RECURSIVE_YES) {
+ QStringList entries = QDir(dir).entryList(QDir::Dirs);
+ for(int i = 0; i < (int)entries.count(); i++) {
+ if(entries[i] != "." && entries[i] != "..") {
+ dirs.append(dir + entries[i] + QDir::separator() + regex);
+ }
+ }
+ }
+ QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regex));
+ for(int i = 0; i < (int)files.count(); i++) {
+ QString file = dir + files[i];
+ if (addFile(file)) {
+ add_depend = true;
+ file_count++;
+ }
+ }
+ }
+ if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir, Qt::CaseInsensitive)) {
+ QFileInfo fi(fileInfo(dir));
+ if(fi.absoluteFilePath() != qmake_getpwd())
+ v["DEPENDPATH"] += fileFixify(dir);
+ }
+ }
+ }
+ if(!file_count) { //shall we try a subdir?
+ QStringList knownDirs = Option::projfile::project_dirs;
+ if(Option::projfile::do_pwd)
+ knownDirs.prepend(".");
+ const QString out_file = fileFixify(Option::output.fileName());
+ for(int i = 0; i < knownDirs.count(); ++i) {
+ QString pd = knownDirs.at(i);
+ if(exists(pd)) {
+ QString newdir = pd;
+ QFileInfo fi(fileInfo(newdir));
+ if(fi.isDir()) {
+ newdir = fileFixify(newdir);
+ QStringList &subdirs = v["SUBDIRS"];
+ if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
+ !subdirs.contains(newdir, Qt::CaseInsensitive)) {
+ subdirs.append(newdir);
+ } else {
+ QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
+ for(int i = 0; i < (int)profiles.count(); i++) {
+ QString nd = newdir;
+ if(nd == ".")
+ nd = "";
+ else if(!nd.isEmpty() && !nd.endsWith(QString(QChar(QDir::separator()))))
+ nd += QDir::separator();
+ nd += profiles[i];
+ fileFixify(nd);
+ if(profiles[i] != "." && profiles[i] != ".." &&
+ !subdirs.contains(nd, Qt::CaseInsensitive) && !out_file.endsWith(nd))
+ subdirs.append(nd);
+ }
+ }
+ if(Option::recursive == Option::QMAKE_RECURSIVE_YES) {
+ QStringList dirs = QDir(newdir).entryList(QDir::Dirs);
+ for(int i = 0; i < (int)dirs.count(); i++) {
+ QString nd = fileFixify(newdir + QDir::separator() + dirs[i]);
+ if(dirs[i] != "." && dirs[i] != ".." && !knownDirs.contains(nd, Qt::CaseInsensitive))
+ knownDirs.append(nd);
+ }
+ }
+ }
+ } else { //regexp
+ QString regx = pd, dir;
+ int s = regx.lastIndexOf(Option::dir_sep);
+ if(s != -1) {
+ dir = regx.left(s+1);
+ regx = regx.right(regx.length() - (s+1));
+ }
+ QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regx), QDir::Dirs);
+ QStringList &subdirs = v["SUBDIRS"];
+ for(int i = 0; i < (int)files.count(); i++) {
+ QString newdir(dir + files[i]);
+ QFileInfo fi(fileInfo(newdir));
+ if(fi.fileName() != "." && fi.fileName() != "..") {
+ newdir = fileFixify(newdir);
+ if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
+ !subdirs.contains(newdir)) {
+ subdirs.append(newdir);
+ } else {
+ QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
+ for(int i = 0; i < (int)profiles.count(); i++) {
+ QString nd = newdir + QDir::separator() + files[i];
+ fileFixify(nd);
+ if(files[i] != "." && files[i] != ".." && !subdirs.contains(nd, Qt::CaseInsensitive)) {
+ if(newdir + files[i] != Option::output_dir + Option::output.fileName())
+ subdirs.append(nd);
+ }
+ }
+ }
+ if(Option::recursive == Option::QMAKE_RECURSIVE_YES
+ && !knownDirs.contains(newdir, Qt::CaseInsensitive))
+ knownDirs.append(newdir);
+ }
+ }
+ }
+ }
+ v["TEMPLATE_ASSIGN"] = QStringList("subdirs");
+ return;
+ }
+
+ //setup deplist
+ QList<QMakeLocalFileName> deplist;
+ {
+ const QStringList &d = v["DEPENDPATH"];
+ for(int i = 0; i < d.size(); ++i)
+ deplist.append(QMakeLocalFileName(d[i]));
+ }
+ setDependencyPaths(deplist);
+
+ QStringList &h = v["HEADERS"];
+ bool no_qt_files = true;
+ QString srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", QString() };
+ for(int i = 0; !srcs[i].isNull(); i++) {
+ const QStringList &l = v[srcs[i]];
+ QMakeSourceFileInfo::SourceFileType type = QMakeSourceFileInfo::TYPE_C;
+ QMakeSourceFileInfo::addSourceFiles(l, QMakeSourceFileInfo::SEEK_DEPS, type);
+ for(int i = 0; i < l.size(); ++i) {
+ QStringList tmp = QMakeSourceFileInfo::dependencies(l[i]);
+ if(!tmp.isEmpty()) {
+ for(int dep_it = 0; dep_it < tmp.size(); ++dep_it) {
+ QString dep = tmp[dep_it];
+ dep = fixPathToQmake(dep);
+ QString file_dir = dep.section(Option::dir_sep, 0, -2),
+ file_no_path = dep.section(Option::dir_sep, -1);
+ if(!file_dir.isEmpty()) {
+ for(int inc_it = 0; inc_it < deplist.size(); ++inc_it) {
+ QMakeLocalFileName inc = deplist[inc_it];
+ if(inc.local() == file_dir && !v["INCLUDEPATH"].contains(inc.real(), Qt::CaseInsensitive))
+ v["INCLUDEPATH"] += inc.real();
+ }
+ }
+ if(no_qt_files && file_no_path.indexOf(QRegExp("^q[a-z_0-9].h$")) != -1)
+ no_qt_files = false;
+ QString h_ext;
+ for(int hit = 0; hit < Option::h_ext.size(); ++hit) {
+ if(dep.endsWith(Option::h_ext.at(hit))) {
+ h_ext = Option::h_ext.at(hit);
+ break;
+ }
+ }
+ if(!h_ext.isEmpty()) {
+ for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
+ QString src(dep.left(dep.length() - h_ext.length()) +
+ Option::cpp_ext.at(cppit));
+ if(exists(src)) {
+ QStringList &srcl = v["SOURCES"];
+ if(!srcl.contains(src, Qt::CaseInsensitive))
+ srcl.append(src);
+ }
+ }
+ } else if(dep.endsWith(Option::lex_ext) &&
+ file_no_path.startsWith(Option::lex_mod)) {
+ addConfig("lex_included");
+ }
+ if(!h.contains(dep, Qt::CaseInsensitive))
+ h += dep;
+ }
+ }
+ }
+ }
+
+ //strip out files that are actually output from internal compilers (ie temporary files)
+ const QStringList &quc = project->variables()["QMAKE_EXTRA_COMPILERS"];
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ QString tmp_out = project->variables()[(*it) + ".output"].first();
+ if(tmp_out.isEmpty())
+ continue;
+
+ QStringList var_out = project->variables()[(*it) + ".variable_out"];
+ bool defaults = var_out.isEmpty();
+ for(int i = 0; i < var_out.size(); ++i) {
+ QString v = var_out.at(i);
+ if(v.startsWith("GENERATED_")) {
+ defaults = true;
+ break;
+ }
+ }
+ if(defaults) {
+ var_out << "SOURCES";
+ var_out << "HEADERS";
+ var_out << "FORMS";
+ }
+ const QStringList &tmp = project->variables()[(*it) + ".input"];
+ for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
+ QStringList &inputs = project->variables()[(*it2)];
+ for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
+ QString path = replaceExtraCompilerVariables(tmp_out, (*input), QString());
+ path = fixPathToQmake(path).section('/', -1);
+ for(int i = 0; i < var_out.size(); ++i) {
+ QString v = var_out.at(i);
+ QStringList &list = project->variables()[v];
+ for(int src = 0; src < list.size(); ) {
+ if(list[src] == path || list[src].endsWith("/" + path))
+ list.removeAt(src);
+ else
+ ++src;
+ }
+ }
+ }
+ }
+ }
+}
+
+bool
+ProjectGenerator::writeMakefile(QTextStream &t)
+{
+ t << "######################################################################" << endl;
+ t << "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl;
+ t << "######################################################################" << endl << endl;
+ if(!Option::user_configs.isEmpty())
+ t << "CONFIG += " << Option::user_configs.join(" ") << endl;
+ int i;
+ for(i = 0; i < Option::before_user_vars.size(); ++i)
+ t << Option::before_user_vars[i] << endl;
+ t << getWritableVar("TEMPLATE_ASSIGN", false);
+ if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
+ t << endl << "# Directories" << "\n"
+ << getWritableVar("SUBDIRS");
+ } else {
+ t << getWritableVar("TARGET_ASSIGN")
+ << getWritableVar("CONFIG", false)
+ << getWritableVar("CONFIG_REMOVE", false)
+ << getWritableVar("DEPENDPATH")
+ << getWritableVar("INCLUDEPATH") << endl;
+
+ t << "# Input" << "\n";
+ t << getWritableVar("HEADERS")
+ << getWritableVar("FORMS")
+ << getWritableVar("LEXSOURCES")
+ << getWritableVar("YACCSOURCES")
+ << getWritableVar("SOURCES")
+ << getWritableVar("RESOURCES")
+ << getWritableVar("TRANSLATIONS");
+ }
+ for(i = 0; i < Option::after_user_vars.size(); ++i)
+ t << Option::after_user_vars[i] << endl;
+ return true;
+}
+
+bool
+ProjectGenerator::addConfig(const QString &cfg, bool add)
+{
+ QString where = "CONFIG";
+ if(!add)
+ where = "CONFIG_REMOVE";
+ if(!project->variables()[where].contains(cfg)) {
+ project->variables()[where] += cfg;
+ return true;
+ }
+ return false;
+}
+
+bool
+ProjectGenerator::addFile(QString file)
+{
+ file = fileFixify(file, qmake_getpwd());
+ QString dir;
+ int s = file.lastIndexOf(Option::dir_sep);
+ if(s != -1)
+ dir = file.left(s+1);
+ if(file.mid(dir.length(), Option::h_moc_mod.length()) == Option::h_moc_mod)
+ return false;
+
+ QString where;
+ for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
+ if(file.endsWith(Option::cpp_ext[cppit])) {
+ where = "SOURCES";
+ break;
+ }
+ }
+ if(where.isEmpty()) {
+ for(int hit = 0; hit < Option::h_ext.size(); ++hit)
+ if(file.endsWith(Option::h_ext.at(hit))) {
+ where = "HEADERS";
+ break;
+ }
+ }
+ if(where.isEmpty()) {
+ for(int cit = 0; cit < Option::c_ext.size(); ++cit) {
+ if(file.endsWith(Option::c_ext[cit])) {
+ where = "SOURCES";
+ break;
+ }
+ }
+ }
+ if(where.isEmpty()) {
+ if(file.endsWith(Option::ui_ext))
+ where = "FORMS";
+ else if(file.endsWith(Option::lex_ext))
+ where = "LEXSOURCES";
+ else if(file.endsWith(Option::yacc_ext))
+ where = "YACCSOURCES";
+ else if(file.endsWith(".ts") || file.endsWith(".xlf"))
+ where = "TRANSLATIONS";
+ else if(file.endsWith(".qrc"))
+ where = "RESOURCES";
+ }
+
+ QString newfile = fixPathToQmake(fileFixify(file));
+
+ QStringList &endList = project->variables()[where];
+ if(!endList.contains(newfile, Qt::CaseInsensitive)) {
+ endList += newfile;
+ return true;
+ }
+ return false;
+}
+
+QString
+ProjectGenerator::getWritableVar(const QString &v, bool)
+{
+ QStringList &vals = project->variables()[v];
+ if(vals.isEmpty())
+ return "";
+
+ // If values contain spaces, ensure that they are quoted
+ for(QStringList::iterator it = vals.begin(); it != vals.end(); ++it) {
+ if ((*it).contains(' ') && !(*it).startsWith(' '))
+ *it = '\"' + *it + '\"';
+ }
+
+ QString ret;
+ if(v.endsWith("_REMOVE"))
+ ret = v.left(v.length() - 7) + " -= ";
+ else if(v.endsWith("_ASSIGN"))
+ ret = v.left(v.length() - 7) + " = ";
+ else
+ ret = v + " += ";
+ QString join = vals.join(" ");
+ if(ret.length() + join.length() > 80) {
+ QString spaces;
+ for(int i = 0; i < ret.length(); i++)
+ spaces += " ";
+ join = vals.join(" \\\n" + spaces);
+ }
+ return ret + join + "\n";
+}
+
+bool
+ProjectGenerator::openOutput(QFile &file, const QString &build) const
+{
+ QString outdir;
+ if(!file.fileName().isEmpty()) {
+ QFileInfo fi(fileInfo(file.fileName()));
+ if(fi.isDir())
+ outdir = fi.path() + QDir::separator();
+ }
+ if(!outdir.isEmpty() || file.fileName().isEmpty()) {
+ QString dir = qmake_getpwd();
+ int s = dir.lastIndexOf('/');
+ if(s != -1)
+ dir = dir.right(dir.length() - (s + 1));
+ file.setFileName(outdir + dir + Option::pro_ext);
+ }
+ return MakefileGenerator::openOutput(file, build);
+}
+
+
+QString
+ProjectGenerator::fixPathToQmake(const QString &file)
+{
+ QString ret = file;
+ if(Option::dir_sep != QLatin1String("/"))
+ ret = ret.replace(Option::dir_sep, QLatin1String("/"));
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/projectgenerator.h b/qmake/generators/projectgenerator.h
new file mode 100644
index 0000000000..7b4d56c3cd
--- /dev/null
+++ b/qmake/generators/projectgenerator.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROJECTGENERATOR_H
+#define PROJECTGENERATOR_H
+
+#include "makefile.h"
+
+QT_BEGIN_NAMESPACE
+
+class ProjectGenerator : public MakefileGenerator
+{
+ bool init_flag;
+ bool addFile(QString);
+ bool addConfig(const QString &, bool add=true);
+ QString getWritableVar(const QString &, bool fixPath=true);
+ QString fixPathToQmake(const QString &file);
+protected:
+ virtual void init();
+ virtual bool writeMakefile(QTextStream &);
+public:
+ ProjectGenerator();
+ ~ProjectGenerator();
+ virtual bool supportsMetaBuild() { return false; }
+ virtual bool openOutput(QFile &, const QString &) const;
+};
+
+inline ProjectGenerator::~ProjectGenerator()
+{ }
+
+QT_END_NAMESPACE
+
+#endif // PROJECTGENERATOR_H
diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.cpp b/qmake/generators/symbian/initprojectdeploy_symbian.cpp
new file mode 100644
index 0000000000..8d04a424c6
--- /dev/null
+++ b/qmake/generators/symbian/initprojectdeploy_symbian.cpp
@@ -0,0 +1,379 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "initprojectdeploy_symbian.h"
+#include <QDirIterator>
+#include <project.h>
+#include <qxmlstream.h>
+#include <qsettings.h>
+#include <qdebug.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+#define SYSBIN_DIR "/sys/bin"
+#define HW_Z_DIR "epoc32/data/z"
+
+#define SUFFIX_DLL "dll"
+#define SUFFIX_EXE "exe"
+#define SUFFIX_QTPLUGIN "qtplugin"
+
+static QString fixPathToEpocOS(const QString &src)
+{
+ QString ret = Option::fixPathToTargetOS(src);
+
+ bool pathHasDriveLetter = false;
+ if (ret.size() > 1)
+ pathHasDriveLetter = (ret.at(1) == QLatin1Char(':'));
+
+ return pathHasDriveLetter ? ret.replace('/', '\\') : QDir::toNativeSeparators(ret);
+}
+
+static bool isPlugin(const QFileInfo& info, const QString& devicePath)
+{
+ // Libraries are plugins if deployment path is something else than
+ // SYSBIN_DIR with or without drive letter
+ if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive)
+ && (devicePath.size() < 8
+ || (0 != devicePath.compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive)
+ && 0 != devicePath.mid(1).compare(QLatin1String(":" SYSBIN_DIR), Qt::CaseInsensitive)
+ && 0 != devicePath.compare(qt_epocRoot() + QLatin1String(HW_Z_DIR SYSBIN_DIR))))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool isBinary(const QFileInfo& info)
+{
+ if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive) ||
+ 0 == info.suffix().compare(QLatin1String(SUFFIX_EXE), Qt::CaseInsensitive)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void createPluginStub(const QFileInfo& info,
+ const QString& devicePath,
+ DeploymentList &deploymentList,
+ QStringList& generatedDirs,
+ QStringList& generatedFiles)
+{
+ QString pluginStubDir = Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_STUB_DIR);
+ QDir().mkpath(pluginStubDir);
+ if (!generatedDirs.contains(pluginStubDir))
+ generatedDirs << pluginStubDir;
+ // Plugin stubs must have different name from the actual plugins, because
+ // the toolchain for creating ROM images cannot handle non-binary .dll files properly.
+ QFile stubFile(pluginStubDir + QLatin1Char('/') + info.completeBaseName() + QLatin1Char('.') + QLatin1String(SUFFIX_QTPLUGIN));
+ if (stubFile.open(QIODevice::WriteOnly)) {
+ if (!generatedFiles.contains(stubFile.fileName()))
+ generatedFiles << stubFile.fileName();
+ QTextStream t(&stubFile);
+ // Add note to stub so that people will not wonder what it is.
+ // Creation date is added to make new stub to deploy always to
+ // force plugin cache miss when loading plugins.
+ t << "This file is a Qt plugin stub file. The real Qt plugin is located in " SYSBIN_DIR ". Created:" << QDateTime::currentDateTime().toString(Qt::ISODate) << "\n";
+ } else {
+ fprintf(stderr, "cannot deploy \"%s\" because of plugin stub file creation failed\n", info.fileName().toLatin1().constData());
+ }
+ QFileInfo stubInfo(stubFile);
+ deploymentList.append(CopyItem(Option::fixPathToLocalOS(stubInfo.absoluteFilePath()),
+ fixPathToEpocOS(devicePath + "/" + stubInfo.fileName())));
+}
+
+QString generate_uid(const QString& target)
+{
+ static QMap<QString, QString> targetToUid;
+
+ QString tmp = targetToUid[target];
+
+ if (!tmp.isEmpty()) {
+ return tmp;
+ }
+
+ quint32 hash = 5381;
+ int c;
+
+ for (int i = 0; i < target.size(); ++i) {
+ c = target.at(i).toAscii();
+ hash ^= c + ((c - i) << i % 20) + ((c + i) << (i + 5) % 20) + ((c - 2 * i) << (i + 10) % 20) + ((c + 2 * i) << (i + 15) % 20);
+ }
+
+ tmp.setNum(hash, 16);
+ for (int i = tmp.size(); i < 8; ++i)
+ tmp.prepend("0");
+
+ targetToUid[target] = tmp;
+
+ return tmp;
+}
+
+// UIDs starting with 0xE are test UIDs in symbian
+QString generate_test_uid(const QString& target)
+{
+ QString tmp = generate_uid(target);
+ tmp.replace(0, 1, "E");
+ tmp.prepend("0x");
+
+ return tmp;
+}
+
+
+void initProjectDeploySymbian(QMakeProject* project,
+ DeploymentList &deploymentList,
+ const QString &testPath,
+ bool deployBinaries,
+ bool epocBuild,
+ const QString &platform,
+ const QString &build,
+ QStringList& generatedDirs,
+ QStringList& generatedFiles)
+{
+ QString targetPath = testPath;
+ if (targetPath.endsWith("/") || targetPath.endsWith("\\"))
+ targetPath = targetPath.mid(0, targetPath.size() - 1);
+
+ bool targetPathHasDriveLetter = false;
+ if (targetPath.size() > 1) {
+ targetPathHasDriveLetter = targetPath.at(1) == QLatin1Char(':');
+ }
+
+ QString deploymentDrive;
+ if (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) {
+ deploymentDrive = qt_epocRoot() + HW_Z_DIR;
+ } else {
+ deploymentDrive = targetPathHasDriveLetter ? targetPath.left(2) : QLatin1String("c:");
+ }
+
+ foreach(QString item, project->values("DEPLOYMENT")) {
+ QString devicePath = project->first(item + ".path");
+ QString devicePathWithoutDrive = devicePath;
+
+ bool devicePathHasDriveLetter = false;
+ if (devicePath.size() > 1) {
+ devicePathHasDriveLetter = devicePath.at(1) == QLatin1Char(':');
+ }
+
+ // Sometimes devicePath can contain disk but APP_RESOURCE_DIR does not,
+ // so remove the drive letter for comparison purposes.
+ if (devicePathHasDriveLetter)
+ {
+ devicePathWithoutDrive.remove(0,2);
+ }
+ if (!deployBinaries
+ && 0 != platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))
+ && !devicePathWithoutDrive.isEmpty()
+ && (0 == devicePathWithoutDrive.compare(project->values("APP_RESOURCE_DIR").join(""), Qt::CaseInsensitive)
+ || 0 == devicePathWithoutDrive.compare(project->values("REG_RESOURCE_IMPORT_DIR").join(""), Qt::CaseInsensitive))) {
+ // Do not deploy resources in emulator builds, as that seems to cause conflicts
+ // If there is ever a real need to deploy pre-built resources for emulator,
+ // BLD_INF_RULES.prj_exports can be used as a workaround.
+ continue;
+ }
+
+ if (devicePath.isEmpty() || devicePath == QLatin1String(".")) {
+ devicePath = targetPath;
+ }
+ // check if item.path is relative (! either / or \)
+ else if (!(devicePath.at(0) == QLatin1Char('/')
+ || devicePath.at(0) == QLatin1Char('\\')
+ || devicePathHasDriveLetter)) {
+ // Create output path
+ devicePath = Option::fixPathToLocalOS(QDir::cleanPath(targetPath + QLatin1Char('/') + devicePath));
+ } else {
+ if (0 == platform.compare(QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM))) {
+ if (devicePathHasDriveLetter) {
+ if (devicePath.startsWith("!"))
+ devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath.remove(0, 2);
+ else
+ devicePath = qt_epocRoot() + "epoc32/winscw/" + devicePath.remove(1, 1);
+ } else {
+ devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath;
+ }
+ } else {
+ if (devicePathHasDriveLetter
+ && 0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) {
+ devicePath.remove(0,2);
+ }
+ if (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))
+ || (!devicePathHasDriveLetter && targetPathHasDriveLetter)) {
+ devicePath = deploymentDrive + devicePath;
+ }
+ }
+ }
+
+ devicePath.replace(QLatin1String("\\"), QLatin1String("/"));
+
+ if (!deployBinaries
+ && 0 == devicePath.right(8).compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive)
+ && 0 != platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) {
+ // Skip deploying to SYSBIN_DIR for anything but binary deployments
+ // Note: Deploying pre-built binaries also follow this rule, so emulator builds
+ // will not get those deployed. Since there is no way to differentiate currently
+ // between pre-built binaries for emulator and HW anyway, this is not a major issue.
+ continue;
+ }
+
+ QStringList flags = project->values(item + ".flags");
+
+ // ### Qt 5: remove .sources, inconsistent with INSTALLS
+ foreach(QString source, project->values(item + ".sources") + project->values(item + ".files")) {
+ source = Option::fixPathToLocalOS(source);
+ QString nameFilter;
+ QFileInfo info(source);
+ QString searchPath;
+ bool dirSearch = false;
+
+ if (info.isDir()) {
+ nameFilter = QLatin1String("*");
+ searchPath = info.absoluteFilePath();
+ dirSearch = true;
+ } else {
+ if (info.exists() || source.indexOf('*') != -1) {
+ nameFilter = source.split(QDir::separator()).last();
+ searchPath = info.absolutePath();
+ } else {
+ // Entry was not found. That is ok if it is a binary, since those do not necessarily yet exist.
+ // Dlls need to be processed even when not deploying binaries for the stubs
+ if (isBinary(info)) {
+ if (deployBinaries) {
+ // Executables and libraries are deployed to \sys\bin
+ QFileInfo targetPath;
+ if (epocBuild)
+ targetPath.setFile(qt_epocRoot() + "epoc32/release/" + platform + "/" + build + "/");
+ else
+ targetPath.setFile(info.path() + QDir::separator());
+ if(devicePathHasDriveLetter) {
+ deploymentList.append(CopyItem(
+ Option::fixPathToLocalOS(targetPath.absolutePath() + "/" + info.fileName(),
+ false, true),
+ fixPathToEpocOS(devicePath.left(2) + QLatin1String(SYSBIN_DIR "/")
+ + info.fileName()),
+ flags));
+ } else {
+ deploymentList.append(CopyItem(
+ Option::fixPathToLocalOS(targetPath.absolutePath() + "/" + info.fileName(),
+ false, true),
+ fixPathToEpocOS(deploymentDrive + QLatin1String("/" SYSBIN_DIR "/")
+ + info.fileName()),
+ flags));
+ }
+ }
+ if (isPlugin(info, devicePath)) {
+ createPluginStub(info, devicePath, deploymentList, generatedDirs, generatedFiles);
+ continue;
+ }
+ } else {
+ // Generate deployment even if file doesn't exist, as this may be the case
+ // when generating .pkg files.
+ deploymentList.append(CopyItem(Option::fixPathToLocalOS(info.absoluteFilePath()),
+ fixPathToEpocOS(devicePath + "/" + info.fileName()),
+ flags));
+ continue;
+ }
+ }
+ }
+
+ int pathSize = info.absolutePath().size();
+ QDirIterator iterator(searchPath, QStringList() << nameFilter
+ , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks
+ , dirSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
+
+ while (iterator.hasNext()) {
+ iterator.next();
+ QFileInfo iteratorInfo(iterator.filePath());
+ QString absoluteItemPath = Option::fixPathToLocalOS(iteratorInfo.absolutePath());
+ int diffSize = absoluteItemPath.size() - pathSize;
+
+ if (!iteratorInfo.isDir()) {
+ if (isPlugin(iterator.fileInfo(), devicePath)) {
+ // This deploys pre-built plugins. Other pre-built binaries will deploy normally,
+ // as they have SYSBIN_DIR target path.
+ if (deployBinaries
+ || (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM)))) {
+ if (devicePathHasDriveLetter) {
+ deploymentList.append(CopyItem(
+ Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()),
+ fixPathToEpocOS(devicePath.left(2) + QLatin1String(SYSBIN_DIR "/")
+ + iterator.fileName()),
+ flags));
+ } else {
+ deploymentList.append(CopyItem(
+ Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()),
+ fixPathToEpocOS(deploymentDrive + QLatin1String("/" SYSBIN_DIR "/")
+ + iterator.fileName()),
+ flags));
+ }
+ }
+ createPluginStub(info, devicePath + "/" + absoluteItemPath.right(diffSize),
+ deploymentList, generatedDirs, generatedFiles);
+ continue;
+ } else {
+ deploymentList.append(CopyItem(
+ Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()),
+ fixPathToEpocOS(devicePath + "/" + absoluteItemPath.right(diffSize)
+ + "/" + iterator.fileName()),
+ flags));
+ }
+ }
+ }
+ }
+ }
+
+ // Remove deployments that do not actually do anything
+ if (0 == platform.compare(QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM))
+ || 0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) {
+ QMutableListIterator<CopyItem> i(deploymentList);
+ while(i.hasNext()) {
+ CopyItem &item = i.next();
+ QFileInfo fromItem(item.from);
+ QFileInfo toItem(item.to);
+#if defined(Q_OS_WIN)
+ if (0 == fromItem.absoluteFilePath().compare(toItem.absoluteFilePath(), Qt::CaseInsensitive))
+#else
+ if (0 == fromItem.absoluteFilePath().compare(toItem.absoluteFilePath()))
+#endif
+ i.remove();
+ }
+ }
+}
diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.h b/qmake/generators/symbian/initprojectdeploy_symbian.h
new file mode 100644
index 0000000000..539f4c4248
--- /dev/null
+++ b/qmake/generators/symbian/initprojectdeploy_symbian.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INITPROJECTDEPLOYSYMBIAN_H
+#define INITPROJECTDEPLOYSYMBIAN_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <option.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <stdlib.h>
+
+#define PLUGIN_STUB_DIR "qmakepluginstubs"
+#define ROM_DEPLOYMENT_PLATFORM "rom"
+#define EMULATOR_DEPLOYMENT_PLATFORM "emulator"
+
+struct CopyItem
+{
+ CopyItem(const QString& f, const QString& t)
+ : from(f) , to(t) { }
+ CopyItem(const QString& f, const QString& t, const QStringList& l)
+ : from(f) , to(t), flags(l) { }
+ QString from;
+ QString to;
+ QStringList flags;
+};
+typedef QList<CopyItem> DeploymentList;
+
+extern QString generate_uid(const QString& target);
+extern QString generate_test_uid(const QString& target);
+
+extern void initProjectDeploySymbian(QMakeProject* project,
+ DeploymentList &deploymentList,
+ const QString &testPath,
+ bool deployBinaries,
+ bool epocBuild,
+ const QString &platform,
+ const QString &build,
+ QStringList& generatedDirs,
+ QStringList& generatedFiles);
+
+#endif // INITPROJECTDEPLOYSYMBIAN_H
diff --git a/qmake/generators/symbian/symbian_makefile.h b/qmake/generators/symbian/symbian_makefile.h
new file mode 100644
index 0000000000..c49845aeb1
--- /dev/null
+++ b/qmake/generators/symbian/symbian_makefile.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIAN_MAKEFILE_H
+#define SYMBIAN_MAKEFILE_H
+
+#include "symbiancommon.h"
+
+// This allows us to reuse the code for both win32 and unix makefile generators.
+template <class T>
+class SymbianMakefileTemplate : public T, public SymbianCommonGenerator
+{
+public:
+ SymbianMakefileTemplate() : SymbianCommonGenerator(this) {}
+
+ void init()
+ {
+ T::init();
+ SymbianCommonGenerator::init();
+ }
+
+ bool writeMakefile(QTextStream &t)
+ {
+ QString numberOfIcons;
+ QString iconFile;
+ QMap<QString, QStringList> userRssRules;
+ readRssRules(numberOfIcons, iconFile, userRssRules);
+
+ // Generate pkg files if there are any actual files to deploy
+ bool generatePkg = false;
+
+ if (targetType == TypeExe) {
+ generatePkg = true;
+ } else {
+ const QStringList deployments = this->project->values("DEPLOYMENT");
+ for (int i = 0; i < deployments.count(); ++i) {
+ // ### Qt 5: remove .sources, inconsistent with INSTALLS
+ if (!this->project->values(deployments.at(i) + ".sources").isEmpty() ||
+ !this->project->values(deployments.at(i) + ".files").isEmpty()) {
+ generatePkg = true;
+ break;
+ }
+ }
+ }
+
+ SymbianLocalizationList symbianLocalizationList;
+ parseTsFiles(&symbianLocalizationList);
+
+ if (generatePkg) {
+ generatePkgFile(iconFile, false, symbianLocalizationList);
+ }
+
+ if (targetType == TypeExe) {
+ if (!this->project->values("CONFIG").contains("no_icon", Qt::CaseInsensitive)) {
+ writeRegRssFile(userRssRules);
+ writeRssFile(numberOfIcons, iconFile);
+ writeLocFile(symbianLocalizationList);
+ }
+ }
+
+ writeCustomDefFile();
+
+ return T::writeMakefile(t);
+ }
+};
+
+#endif // SYMBIAN_MAKEFILE_H
diff --git a/qmake/generators/symbian/symbiancommon.cpp b/qmake/generators/symbian/symbiancommon.cpp
new file mode 100644
index 0000000000..32b465b4bf
--- /dev/null
+++ b/qmake/generators/symbian/symbiancommon.cpp
@@ -0,0 +1,1117 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symbiancommon.h"
+#include <qdebug.h>
+#include <qxmlstream.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+#define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\"
+
+#define RSS_RULES "RSS_RULES"
+#define RSS_RULES_BASE "RSS_RULES."
+#define RSS_TAG_NBROFICONS "number_of_icons"
+#define RSS_TAG_ICONFILE "icon_file"
+#define RSS_TAG_HEADER "header"
+#define RSS_TAG_SERVICE_LIST "service_list"
+#define RSS_TAG_FILE_OWNERSHIP_LIST "file_ownership_list"
+#define RSS_TAG_DATATYPE_LIST "datatype_list"
+#define RSS_TAG_FOOTER "footer"
+#define RSS_TAG_DEFAULT "default_rules" // Same as just giving rules without tag
+
+#define PLUGIN_COMMON_DEF_FILE_ACTUAL "plugin_commonu.def"
+
+#define MANUFACTURER_NOTE_FILE "manufacturer_note.txt"
+#define DEFAULT_MANUFACTURER_NOTE \
+ "The package is not supported for devices from this manufacturer. Please try the selfsigned " \
+ "version of the package instead."
+
+SymbianCommonGenerator::SymbianCommonGenerator(MakefileGenerator *generator)
+ : generator(generator)
+{
+}
+
+void SymbianCommonGenerator::init()
+{
+ QMakeProject *project = generator->project;
+ fixedTarget = project->first("QMAKE_ORIG_TARGET");
+ if (fixedTarget.isEmpty())
+ fixedTarget = project->first("TARGET");
+ fixedTarget = generator->unescapeFilePath(fixedTarget);
+ fixedTarget = removePathSeparators(fixedTarget);
+ removeSpecialCharacters(fixedTarget);
+
+ // This should not be empty since the mkspecs are supposed to set it if missing.
+ uid3 = project->first("TARGET.UID3").trimmed();
+
+ if ((project->values("TEMPLATE")).contains("app"))
+ targetType = TypeExe;
+ else if ((project->values("TEMPLATE")).contains("lib")) {
+ // Check CONFIG to see if we are to build staticlib or dll
+ if (project->isActiveConfig("staticlib") || project->isActiveConfig("static"))
+ targetType = TypeLib;
+ else if (project->isActiveConfig("plugin"))
+ targetType = TypePlugin;
+ else
+ targetType = TypeDll;
+ } else {
+ targetType = TypeSubdirs;
+ }
+
+ // UID is valid as either hex or decimal, so just convert it to number and back to hex
+ // to get proper string for private dir
+ bool conversionOk = false;
+ uint uidNum = uid3.toUInt(&conversionOk, 0);
+
+ if (!conversionOk) {
+ fprintf(stderr, "Error: Invalid UID \"%s\".\n", uid3.toUtf8().constData());
+ } else {
+ privateDirUid.setNum(uidNum, 16);
+ while (privateDirUid.length() < 8)
+ privateDirUid.insert(0, QLatin1Char('0'));
+ }
+}
+
+bool SymbianCommonGenerator::containsStartWithItem(const QChar &c, const QStringList& src)
+{
+ bool result = false;
+ foreach(QString str, src) {
+ if (str.startsWith(c)) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+void SymbianCommonGenerator::removeSpecialCharacters(QString& str)
+{
+ // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf
+ QString underscore = QLatin1String("_");
+ str.replace(QLatin1String("/"), underscore);
+ str.replace(QLatin1String("\\"), underscore);
+ str.replace(QLatin1String(" "), underscore);
+ str.replace(QLatin1String(":"), underscore);
+}
+
+QString romPath(const QString& path)
+{
+ if(path.length() > 2 && path[1] == ':')
+ return QLatin1String("z:") + path.mid(2);
+ return QLatin1String("z:") + path;
+}
+
+void SymbianCommonGenerator::generatePkgFile(const QString &iconFile,
+ bool epocBuild,
+ const SymbianLocalizationList &symbianLocalizationList)
+{
+ QMakeProject *project = generator->project;
+ QString pkgFilename = Option::output_dir + QLatin1Char('/') +
+ QString("%1_template.pkg").arg(fixedTarget);
+
+ QFile pkgFile(pkgFilename);
+ if (!pkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(pkgFilename);
+ return;
+ }
+
+ QString stubPkgFileName = Option::output_dir + QLatin1Char('/') +
+ QString("%1_stub.pkg").arg(fixedTarget);
+
+ QFile stubPkgFile(stubPkgFileName);
+ if (!stubPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(stubPkgFileName);
+ return;
+ }
+
+ generatedFiles << pkgFile.fileName();
+ QTextStream t(&pkgFile);
+ generatedFiles << stubPkgFile.fileName();
+ QTextStream ts(&stubPkgFile);
+
+ QString installerSisHeader = project->values("DEPLOYMENT.installer_header").join("\n");
+ if (installerSisHeader.isEmpty()) {
+ // Use correct protected UID for publishing if application UID is in protected range,
+ // otherwise use self-signable test UID.
+ QRegExp protUidMatcher("0[xX][0-7].*");
+ if (protUidMatcher.exactMatch(uid3))
+ installerSisHeader = QLatin1String("0x2002CCCF");
+ else
+ installerSisHeader = QLatin1String("0xA000D7CE"); // Use default self-signable UID
+ }
+
+ QString wrapperStreamBuffer;
+ QTextStream tw(&wrapperStreamBuffer);
+
+ QString dateStr = QDateTime::currentDateTime().toString(Qt::ISODate);
+
+ // Header info
+ QString wrapperPkgFilename = Option::output_dir + QLatin1Char('/') + QString("%1_installer.%2")
+ .arg(fixedTarget).arg("pkg");
+
+ QString headerComment = "; %1 generated by qmake at %2\n"
+ "; This file is generated by qmake and should not be modified by the user\n"
+ ";\n\n";
+ t << headerComment.arg(pkgFilename).arg(dateStr);
+ tw << headerComment.arg(wrapperPkgFilename).arg(dateStr);
+ ts << headerComment.arg(stubPkgFileName).arg(dateStr);
+
+ QStringList commonRawPreRules;
+ QStringList mainRawPreRules;
+ QStringList instRawPreRules;
+ QStringList stubRawPreRules;
+
+ // Though there can't be more than one language or header line, use stringlists
+ // in case user wants comments to go with the rules.
+ // Note that it makes no sense to have file specific language or header rules,
+ // except what is provided for installer header via "DEPLOYMENT.installer_header" variable,
+ // because stub and main headers should always match. Vendor rules are similarly limited to
+ // make code cleaner as it is unlikely anyone will want different vendor in different files.
+ QStringList languageRules;
+ QStringList headerRules;
+ QStringList vendorRules;
+
+ QStringList commonRawPostRules;
+ QStringList mainRawPostRules;
+ QStringList instRawPostRules;
+ QStringList stubRawPostRules;
+
+ QStringList failList; // Used for detecting incorrect usage
+
+ QString emptySuffix;
+ QString mainSuffix(".main");
+ QString instSuffix(".installer");
+ QString stubSuffix(".stub");
+
+ foreach(QString item, project->values("DEPLOYMENT")) {
+ parsePreRules(item, emptySuffix, &commonRawPreRules, &languageRules, &headerRules, &vendorRules);
+ parsePreRules(item, mainSuffix, &mainRawPreRules, &failList, &failList, &failList);
+ parsePreRules(item, instSuffix, &instRawPreRules, &failList, &failList, &failList);
+ parsePreRules(item, stubSuffix, &stubRawPreRules, &failList, &failList, &failList);
+
+ parsePostRules(item, emptySuffix, &commonRawPostRules);
+ parsePostRules(item, mainSuffix, &mainRawPostRules);
+ parsePostRules(item, instSuffix, &instRawPostRules);
+ parsePostRules(item, stubSuffix, &stubRawPostRules);
+ }
+
+ if (!failList.isEmpty()) {
+ fprintf(stderr, "Warning: Custom language, header, or vendor definitions are not "
+ "supported by file specific pkg_prerules.* variables.\n"
+ "Use plain pkg_prerules and/or DEPLOYMENT.installer_header for customizing "
+ "these items.\n");
+ }
+
+ foreach(QString item, commonRawPreRules) {
+ if (item.startsWith("(")) {
+ // Only regular pkg file should have package dependencies
+ mainRawPreRules << item;
+ } else if (item.startsWith("[")) {
+ // stub pkg file should not have platform dependencies
+ mainRawPreRules << item;
+ instRawPreRules << item;
+ } else {
+ mainRawPreRules << item;
+ instRawPreRules << item;
+ stubRawPreRules << item;
+ }
+ }
+
+ // Currently common postrules only go to main
+ mainRawPostRules << commonRawPostRules;
+
+ // Apply some defaults if specific data does not exist in PKG pre-rules
+ if (languageRules.isEmpty()) {
+ if (symbianLocalizationList.isEmpty()) {
+ languageRules << "; Language\n&EN\n\n";
+ } else {
+ QStringList langCodes;
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ langCodes << loc.symbianLanguageCode;
+ }
+ languageRules << QString("; Languages\n&%1\n\n").arg(langCodes.join(","));
+ }
+ } else if (headerRules.isEmpty()) {
+ // In case user defines langs, he must take care also about SIS header
+ fprintf(stderr, "Warning: If language is defined with DEPLOYMENT pkg_prerules, also the SIS header must be defined\n");
+ }
+
+ t << languageRules.join("\n") << endl;
+ tw << languageRules.join("\n") << endl;
+ ts << languageRules.join("\n") << endl;
+
+ // Determine application version. If version has missing component values,
+ // those will default to zero.
+ // If VERSION is missing altogether or is invalid, use "1,0,0"
+ QStringList verNumList = project->first("VERSION").split('.');
+ uint major = 0;
+ uint minor = 0;
+ uint patch = 0;
+ bool success = false;
+
+ if (verNumList.size() > 0) {
+ major = verNumList[0].toUInt(&success);
+ if (success && verNumList.size() > 1) {
+ minor = verNumList[1].toUInt(&success);
+ if (success && verNumList.size() > 2) {
+ patch = verNumList[2].toUInt(&success);
+ }
+ }
+ }
+
+ QString applicationVersion("1,0,0");
+ if (success)
+ applicationVersion = QString("%1,%2,%3").arg(major).arg(minor).arg(patch);
+
+ // Append package build version number if it is set
+ QString pkgBuildVersion = project->first("DEPLOYMENT.pkg_build_version");
+ if (!pkgBuildVersion.isEmpty()) {
+ success = false;
+ uint build = pkgBuildVersion.toUInt(&success);
+ if (success && build < 100) {
+ if (pkgBuildVersion.size() == 1)
+ pkgBuildVersion.prepend(QLatin1Char('0'));
+ applicationVersion.append(pkgBuildVersion);
+ } else {
+ fprintf(stderr, "Warning: Invalid DEPLOYMENT.pkg_build_version (%s), must be a number between 0 - 99\n", qPrintable(pkgBuildVersion));
+ }
+ }
+
+ // Package header
+ QString sisHeader = "; SIS header: name, uid, version\n#{\"%1\"},(%2),%3\n\n";
+
+ QString defaultVisualTarget = project->values("DEPLOYMENT.display_name").join(" ");
+ if (defaultVisualTarget.isEmpty())
+ defaultVisualTarget = generator->escapeFilePath(project->first("TARGET"));
+ defaultVisualTarget = removePathSeparators(defaultVisualTarget);
+
+ QString visualTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, false);
+ QString wrapperTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, true);
+
+ if (installerSisHeader.startsWith("0x", Qt::CaseInsensitive)) {
+ tw << sisHeader.arg(wrapperTarget).arg(installerSisHeader).arg(applicationVersion);
+ } else {
+ tw << installerSisHeader << endl;
+ }
+
+ if (headerRules.isEmpty()) {
+ t << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
+ ts << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion);
+ }
+ else {
+ t << headerRules.join("\n") << endl;
+ ts << headerRules.join("\n") << endl;
+ }
+
+ // Vendor name
+ if (!containsStartWithItem('%', vendorRules)) {
+ QString vendorStr = QLatin1String("\"Vendor\",");
+ QString locVendors = vendorStr;
+ for (int i = 1; i < symbianLocalizationList.size(); i++) {
+ locVendors.append(vendorStr);
+ }
+ locVendors.chop(1);
+ vendorRules << QString("; Default localized vendor name\n%{%1}\n\n").arg(locVendors);
+ }
+ if (!containsStartWithItem(':', vendorRules)) {
+ vendorRules << "; Default unique vendor name\n:\"Vendor\"\n\n";
+ }
+
+ t << vendorRules.join("\n") << endl;
+ tw << vendorRules.join("\n") << endl;
+ ts << vendorRules.join("\n") << endl;
+
+ // PKG pre-rules - these are added before actual file installations i.e. SIS package body
+ QString comment = "\n; Manual PKG pre-rules from PRO files\n";
+
+ if (mainRawPreRules.size()) {
+ t << comment;
+ t << mainRawPreRules.join("\n") << endl;
+ }
+ if (instRawPreRules.size()) {
+ tw << comment;
+ tw << instRawPreRules.join("\n") << endl;
+ }
+ if (stubRawPreRules.size()) {
+ ts << comment;
+ ts << stubRawPreRules.join("\n") << endl;
+ }
+
+ t << endl;
+ tw << endl;
+ ts << endl;
+
+ // Begin Manufacturer block
+ if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
+ QString manufacturerStr("IF ");
+ foreach(QString manufacturer, project->values("DEPLOYMENT.manufacturers")) {
+ manufacturerStr.append(QString("(MANUFACTURER)=(%1) OR \n ").arg(manufacturer));
+ }
+ // Remove the final OR
+ manufacturerStr.chop(8);
+ t << manufacturerStr << endl;
+ }
+
+ if (symbianLocalizationList.size()) {
+ // Add localized resources to DEPLOYMENT if default resource deployment is done
+ addLocalizedResourcesToDeployment("default_resource_deployment.files", symbianLocalizationList);
+ addLocalizedResourcesToDeployment("default_reg_deployment.files", symbianLocalizationList);
+ }
+
+ // deploy files specified by DEPLOYMENT variable
+ QString remoteTestPath;
+ QString zDir;
+ remoteTestPath = QString("!:\\private\\%1").arg(privateDirUid);
+ if (epocBuild)
+ zDir = qt_epocRoot() + QLatin1String("epoc32/data/z");
+
+ DeploymentList depList;
+ initProjectDeploySymbian(project, depList, remoteTestPath, true, epocBuild, "$(PLATFORM)", "$(TARGET)", generatedDirs, generatedFiles);
+ if (depList.size())
+ t << "; DEPLOYMENT" << endl;
+ for (int i = 0; i < depList.size(); ++i) {
+ QString from = depList.at(i).from;
+ QString to = depList.at(i).to;
+ QString flags;
+ bool showOnlyFile = false;
+ foreach(QString flag, depList.at(i).flags) {
+ if (flag == QLatin1String("FT")
+ || flag == QLatin1String("FILETEXT")) {
+ showOnlyFile = true;
+ }
+ flags.append(QLatin1Char(',')).append(flag);
+ }
+
+ if (epocBuild) {
+ // Deploy anything not already deployed from under epoc32 instead from under
+ // \epoc32\data\z\ to enable using pkg file without rebuilding
+ // the project, which can be useful for some binary only distributions.
+ if (!from.contains(QLatin1String("epoc32"), Qt::CaseInsensitive)) {
+ from = to;
+ if (from.size() > 1 && from.at(1) == QLatin1Char(':'))
+ from = from.mid(2);
+ from.prepend(zDir);
+ }
+ }
+
+ // Files with "FILETEXT"/"FT" flag are meant for showing only at installation time
+ // and therefore do not belong to the stub package and will not install the file into phone.
+ if (showOnlyFile)
+ to.clear();
+ else
+ ts << QString("\"\" - \"%1\"").arg(romPath(to)) << endl;
+
+ t << QString("\"%1\" - \"%2\"%3").arg(from.replace('\\','/')).arg(to).arg(flags) << endl;
+
+ }
+ t << endl;
+ ts << endl;
+
+ // PKG post-rules - these are added after actual file installations i.e. SIS package body
+ comment = "; Manual PKG post-rules from PRO files\n";
+
+ if (mainRawPostRules.size()) {
+ t << comment;
+ t << mainRawPostRules.join("\n") << endl;
+ }
+ if (instRawPostRules.size()) {
+ tw << comment;
+ tw << instRawPostRules.join("\n") << endl;
+ }
+ if (stubRawPostRules.size()) {
+ ts << comment;
+ ts << stubRawPostRules.join("\n") << endl;
+ }
+
+ // Close Manufacturer block
+ if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) {
+ QString manufacturerFailNoteFile;
+ if (project->values("DEPLOYMENT.manufacturers.fail_note").isEmpty()) {
+ manufacturerFailNoteFile = QString("%1_" MANUFACTURER_NOTE_FILE).arg(uid3);
+ QFile ft(manufacturerFailNoteFile);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t2(&ft);
+
+ t2 << QString(DEFAULT_MANUFACTURER_NOTE) << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(manufacturerFailNoteFile)
+ }
+ } else {
+ manufacturerFailNoteFile = project->values("DEPLOYMENT.manufacturers.fail_note").join("");
+ }
+
+ t << "ELSEIF NOT(0) ; MANUFACTURER" << endl
+ << "\"" << generator->fileInfo(manufacturerFailNoteFile).absoluteFilePath() << "\""
+ << " - \"\", FILETEXT, TEXTEXIT" << endl
+ << "ENDIF ; MANUFACTURER" << endl;
+ }
+
+ // Write wrapper pkg
+ if (!installerSisHeader.isEmpty()) {
+ QFile wrapperPkgFile(wrapperPkgFilename);
+ if (!wrapperPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ PRINT_FILE_CREATE_ERROR(wrapperPkgFilename);
+ return;
+ }
+
+ generatedFiles << wrapperPkgFile.fileName();
+ QTextStream twf(&wrapperPkgFile);
+
+ twf << wrapperStreamBuffer << endl;
+
+ // Wrapped files deployment
+ QString currentPath = qmake_getpwd();
+ QString sisName = QString("%1.sis").arg(fixedTarget);
+ twf << "\"" << currentPath << "/" << sisName << "\" - \"!:\\private\\2002CCCE\\import\\" << sisName << "\"" << endl;
+
+ QString bootStrapPath = QLibraryInfo::location(QLibraryInfo::PrefixPath);
+ bootStrapPath.append("/smartinstaller.sis");
+ QFileInfo fi(generator->fileInfo(bootStrapPath));
+ twf << "@\"" << fi.absoluteFilePath() << "\",(0x2002CCCD)" << endl;
+ }
+}
+
+QString SymbianCommonGenerator::removePathSeparators(QString &file)
+{
+ QString ret = file;
+
+ if (QDir::separator().unicode() != '/')
+ ret.replace(QDir::separator(), QLatin1Char('/'));
+
+ if (ret.indexOf(QLatin1Char('/')) >= 0)
+ ret.remove(0, ret.lastIndexOf(QLatin1Char('/')) + 1);
+
+ return ret;
+}
+
+void SymbianCommonGenerator::writeRegRssFile(QMap<QString, QStringList> &userItems)
+{
+ QString filename(fixedTarget);
+ filename.append("_reg.rss");
+ if (!Option::output_dir.isEmpty())
+ filename = Option::output_dir + '/' + filename;
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "#include <" << fixedTarget << ".rsg>" << endl;
+ t << "#include <appinfo.rh>" << endl;
+ foreach(QString item, userItems[RSS_TAG_HEADER])
+ t << item << endl;
+ t << endl;
+ t << "UID2 KUidAppRegistrationResourceFile" << endl;
+ t << "UID3 " << uid3 << endl << endl;
+ t << "RESOURCE APP_REGISTRATION_INFO" << endl;
+ t << "\t{" << endl;
+ t << "\tapp_file=\"" << fixedTarget << "\";" << endl;
+ t << "\tlocalisable_resource_file=\"" RESOURCE_DIRECTORY_RESOURCE << fixedTarget << "\";" << endl;
+
+ writeRegRssList(t, userItems[RSS_TAG_SERVICE_LIST],
+ QLatin1String(RSS_TAG_SERVICE_LIST),
+ QLatin1String("SERVICE_INFO"));
+ writeRegRssList(t, userItems[RSS_TAG_FILE_OWNERSHIP_LIST],
+ QLatin1String(RSS_TAG_FILE_OWNERSHIP_LIST),
+ QLatin1String("FILE_OWNERSHIP_INFO"));
+ writeRegRssList(t, userItems[RSS_TAG_DATATYPE_LIST],
+ QLatin1String(RSS_TAG_DATATYPE_LIST),
+ QLatin1String("DATATYPE"));
+ t << endl;
+
+ foreach(QString item, userItems[RSS_TAG_DEFAULT])
+ t << "\t" << item.replace("\n","\n\t") << endl;
+ t << "\t}" << endl;
+
+ foreach(QString item, userItems[RSS_TAG_FOOTER])
+ t << item << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename)
+ }
+}
+
+void SymbianCommonGenerator::writeRegRssList(QTextStream &t,
+ QStringList &userList,
+ const QString &listTag,
+ const QString &listItem)
+{
+ int itemCount = userList.count();
+ if (itemCount) {
+ t << "\t" << listTag << " ="<< endl;
+ t << "\t\t{" << endl;
+ foreach(QString item, userList) {
+ t << "\t\t" << listItem << endl;
+ t << "\t\t\t{" << endl;
+ t << "\t\t\t" << item.replace("\n","\n\t\t\t") << endl;
+ t << "\t\t\t}";
+ if (--itemCount)
+ t << ",";
+ t << endl;
+ }
+ t << "\t\t}; "<< endl;
+ }
+}
+
+void SymbianCommonGenerator::writeRssFile(QString &numberOfIcons, QString &iconFile)
+{
+ QString filename(fixedTarget);
+ if (!Option::output_dir.isEmpty())
+ filename = Option::output_dir + '/' + filename;
+ filename.append(".rss");
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "CHARACTER_SET UTF8" << endl;
+ t << "#include <appinfo.rh>" << endl;
+ t << "#include \"" << fixedTarget << ".loc\"" << endl;
+ t << endl;
+ t << "RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info" << endl;
+ t << "\t{" << endl;
+ t << "\tshort_caption = STRING_r_short_caption;" << endl;
+ t << "\tcaption_and_icon =" << endl;
+ t << "\tCAPTION_AND_ICON_INFO" << endl;
+ t << "\t\t{" << endl;
+ t << "\t\tcaption = STRING_r_caption;" << endl;
+
+ QString rssIconFile = iconFile;
+ rssIconFile = rssIconFile.replace("/", "\\\\");
+
+ if (numberOfIcons.isEmpty() || rssIconFile.isEmpty()) {
+ // There can be maximum one item in this tag, validated when parsed
+ t << "\t\tnumber_of_icons = 0;" << endl;
+ t << "\t\ticon_file = \"\";" << endl;
+ } else {
+ // There can be maximum one item in this tag, validated when parsed
+ t << "\t\tnumber_of_icons = " << numberOfIcons << ";" << endl;
+ t << "\t\ticon_file = \"" << rssIconFile << "\";" << endl;
+ }
+ t << "\t\t};" << endl;
+ t << "\t}" << endl;
+ t << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename);
+ }
+}
+
+void SymbianCommonGenerator::writeLocFile(const SymbianLocalizationList &symbianLocalizationList)
+{
+ QString filename = generateLocFileName();
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+
+ QString displayName = generator->project->values("DEPLOYMENT.display_name").join(" ");
+ if (displayName.isEmpty())
+ displayName = generator->escapeFilePath(generator->project->first("TARGET"));
+
+ t << "// ============================================================================" << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+ t << "#ifdef LANGUAGE_SC" << endl;
+ t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
+ t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
+
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ QString shortCaption = loc.shortCaption;
+ QString longCaption = loc.longCaption;
+ if (shortCaption.isEmpty())
+ shortCaption = displayName;
+ if (longCaption.isEmpty())
+ longCaption = displayName;
+
+ t << "#elif defined LANGUAGE_" << loc.symbianLanguageCode << endl;
+ t << "#define STRING_r_short_caption \"" << shortCaption << "\"" << endl;
+ t << "#define STRING_r_caption \"" << longCaption << "\"" << endl;
+ }
+
+ t << "#else" << endl;
+ t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl;
+ t << "#define STRING_r_caption \"" << displayName << "\"" << endl;
+ t << "#endif" << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename);
+ }
+}
+
+void SymbianCommonGenerator::readRssRules(QString &numberOfIcons,
+ QString &iconFile, QMap<QString,
+ QStringList> &userRssRules)
+{
+ QMakeProject *project = generator->project;
+ for (QMap<QString, QStringList>::iterator it = project->variables().begin(); it != project->variables().end(); ++it) {
+ if (it.key().startsWith(RSS_RULES_BASE)) {
+ QString newKey = it.key().mid(sizeof(RSS_RULES_BASE) - 1);
+ if (newKey.isEmpty()) {
+ fprintf(stderr, "Warning: Empty RSS_RULES_BASE key encountered\n");
+ continue;
+ }
+ QStringList newValues;
+ QStringList values = it.value();
+ foreach(QString item, values) {
+ // If there is no stringlist defined for a rule, use rule value directly
+ // This is convenience for defining single line statements
+ if (project->values(item).isEmpty()) {
+ newValues << item;
+ } else {
+ QStringList itemList;
+ foreach(QString itemRow, project->values(item)) {
+ itemList << itemRow;
+ }
+ newValues << itemList.join("\n");
+ }
+ }
+ // Verify that there is exactly one value in RSS_TAG_NBROFICONS
+ if (newKey == RSS_TAG_NBROFICONS) {
+ if (newValues.count() == 1) {
+ numberOfIcons = newValues[0];
+ } else {
+ fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS);
+ continue;
+ }
+ // Verify that there is exactly one value in RSS_TAG_ICONFILE
+ } else if (newKey == RSS_TAG_ICONFILE) {
+ if (newValues.count() == 1) {
+ iconFile = newValues[0];
+ } else {
+ fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n",
+ RSS_RULES_BASE, RSS_TAG_ICONFILE);
+ continue;
+ }
+ } else if (newKey == RSS_TAG_HEADER
+ || newKey == RSS_TAG_SERVICE_LIST
+ || newKey == RSS_TAG_FILE_OWNERSHIP_LIST
+ || newKey == RSS_TAG_DATATYPE_LIST
+ || newKey == RSS_TAG_FOOTER
+ || newKey == RSS_TAG_DEFAULT) {
+ userRssRules[newKey] = newValues;
+ continue;
+ } else {
+ fprintf(stderr, "Warning: Unsupported key:'%s%s'\n",
+ RSS_RULES_BASE, newKey.toLatin1().constData());
+ continue;
+ }
+ }
+ }
+
+ QStringList newValues;
+ foreach(QString item, project->values(RSS_RULES)) {
+ // If there is no stringlist defined for a rule, use rule value directly
+ // This is convenience for defining single line statements
+ if (project->values(item).isEmpty()) {
+ newValues << item;
+ } else {
+ newValues << project->values(item);
+ }
+ }
+ userRssRules[RSS_TAG_DEFAULT] << newValues;
+
+ // Validate that either both RSS_TAG_NBROFICONS and RSS_TAG_ICONFILE keys exist
+ // or neither of them exist
+ if (!((numberOfIcons.isEmpty() && iconFile.isEmpty()) ||
+ (!numberOfIcons.isEmpty() && !iconFile.isEmpty()))) {
+ numberOfIcons.clear();
+ iconFile.clear();
+ fprintf(stderr, "Warning: Both or neither of '%s%s' and '%s%s' keys must exist.\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS, RSS_RULES_BASE, RSS_TAG_ICONFILE);
+ }
+
+ // Validate that RSS_TAG_NBROFICONS contains only numbers
+ if (!numberOfIcons.isEmpty()) {
+ bool ok;
+ numberOfIcons = numberOfIcons.simplified();
+ numberOfIcons.toInt(&ok);
+ if (!ok) {
+ numberOfIcons.clear();
+ iconFile.clear();
+ fprintf(stderr, "Warning: '%s%s' must be integer in decimal format.\n",
+ RSS_RULES_BASE, RSS_TAG_NBROFICONS);
+ }
+ }
+}
+
+void SymbianCommonGenerator::writeCustomDefFile()
+{
+ if (targetType == TypePlugin && !generator->project->isActiveConfig("stdbinary")) {
+ // Create custom def file for plugin
+ QFile ft(Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_COMMON_DEF_FILE_ACTUAL));
+
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+
+ t << "; ==============================================================================" << endl;
+ t << "; Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "; This file is generated by qmake and should not be modified by the" << endl;
+ t << "; user." << endl;
+ t << "; Name : " PLUGIN_COMMON_DEF_FILE_ACTUAL << endl;
+ t << "; Part of : " << generator->project->values("TARGET").join(" ") << endl;
+ t << "; Description : Fixes common plugin symbols to known ordinals" << endl;
+ t << "; Version : " << endl;
+ t << ";" << endl;
+ t << "; ==============================================================================" << "\n" << endl;
+
+ t << endl;
+
+ t << "EXPORTS" << endl;
+ t << "\tqt_plugin_query_verification_data @ 1 NONAME" << endl;
+ t << "\tqt_plugin_instance @ 2 NONAME" << endl;
+ t << endl;
+ } else {
+ PRINT_FILE_CREATE_ERROR(QString(PLUGIN_COMMON_DEF_FILE_ACTUAL))
+ }
+ }
+}
+
+void SymbianCommonGenerator::parseTsFiles(SymbianLocalizationList *symbianLocalizationList)
+{
+ if (!generator->project->isActiveConfig("localize_deployment")) {
+ return;
+ }
+
+ QStringList symbianTsFiles;
+
+ symbianTsFiles << generator->project->values("SYMBIAN_MATCHED_TRANSLATIONS");
+
+ if (!symbianTsFiles.isEmpty()) {
+ fillQt2SymbianLocalizationList(symbianLocalizationList);
+
+ QMutableListIterator<SymbianLocalization> iter(*symbianLocalizationList);
+ while (iter.hasNext()) {
+ SymbianLocalization &loc = iter.next();
+ static QString matchStrTemplate = QLatin1String(".*_%1\\.ts");
+ QString matchStr = matchStrTemplate.arg(loc.qtLanguageCode);
+
+ foreach (QString file, symbianTsFiles) {
+ QRegExp matcher(matchStr);
+ if (matcher.exactMatch(file) && parseTsContent(file, &loc))
+ break;
+ }
+ }
+ }
+}
+
+void SymbianCommonGenerator::fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList)
+{
+ static QString symbianCodePrefix = QLatin1String("SYMBIAN_LANG.");
+
+ QStringList symbianLanguages = generator->project->values("SYMBIAN_MATCHED_LANGUAGES");
+
+ foreach (QString qtCode, symbianLanguages) {
+ SymbianLocalization newLoc;
+ QString symbianCodeVariable = symbianCodePrefix + qtCode;
+ newLoc.symbianLanguageCode = generator->project->first(symbianCodeVariable);
+ if (!newLoc.symbianLanguageCode.isEmpty()) {
+ newLoc.qtLanguageCode = qtCode;
+ symbianLocalizationList->append(newLoc);
+ }
+ }
+}
+
+void SymbianCommonGenerator::parsePreRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList,
+ QStringList *languageRuleList,
+ QStringList *headerRuleList,
+ QStringList *vendorRuleList)
+{
+ QMakeProject *project = generator->project;
+ foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_prerules" + variableSuffix)) {
+ QStringList pkgrulesValue = project->values(pkgrulesItem);
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line statements
+ if (pkgrulesValue.isEmpty()) {
+ if (pkgrulesItem.startsWith("&"))
+ *languageRuleList << pkgrulesItem;
+ else if (pkgrulesItem.startsWith("#"))
+ *headerRuleList << pkgrulesItem;
+ else if (pkgrulesItem.startsWith("%") || pkgrulesItem.startsWith(":"))
+ *vendorRuleList << pkgrulesItem;
+ else
+ *rawRuleList << pkgrulesItem;
+ } else {
+ if (containsStartWithItem('&', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *languageRuleList << pkgrule;
+ }
+ } else if (containsStartWithItem('#', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *headerRuleList << pkgrule;
+ }
+ } else if (containsStartWithItem('%', pkgrulesValue)
+ || containsStartWithItem(':', pkgrulesValue)) {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *vendorRuleList << pkgrule;
+ }
+ } else {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *rawRuleList << pkgrule;
+ }
+ }
+ }
+ }
+}
+
+void SymbianCommonGenerator::parsePostRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList)
+{
+ QMakeProject *project = generator->project;
+ foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_postrules" + variableSuffix)) {
+ QStringList pkgrulesValue = project->values(pkgrulesItem);
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line statements
+ if (pkgrulesValue.isEmpty()) {
+ *rawRuleList << pkgrulesItem;
+ } else {
+ foreach(QString pkgrule, pkgrulesValue) {
+ *rawRuleList << pkgrule;
+ }
+ }
+ }
+}
+
+bool SymbianCommonGenerator::parseTsContent(const QString &tsFilename, SymbianLocalization *loc)
+{
+ bool retval = true;
+ QMakeProject *project = generator->project;
+ QFile tsFile(tsFilename);
+
+ if (tsFile.exists()) {
+ if (tsFile.open(QIODevice::ReadOnly)) {
+ static QString applicationCaptionsContext = QLatin1String("QtApplicationCaptions");
+ static QString pkgNameContext = QLatin1String("QtPackageNames");
+ static QString tsElement = QLatin1String("TS");
+ static QString contextElement = QLatin1String("context");
+ static QString nameElement = QLatin1String("name");
+ static QString messageElement = QLatin1String("message");
+ static QString sourceElement = QLatin1String("source");
+ static QString translationElement = QLatin1String("translation");
+ static QString shortCaptionId = QLatin1String("Application short caption");
+ static QString longCaptionId = QLatin1String("Application long caption");
+ static QString pkgDisplayNameId = QLatin1String("Package name");
+ static QString installerPkgDisplayNameId = QLatin1String("Smart installer package name");
+ static QString languageAttribute = QLatin1String("language");
+ static QChar underscoreChar = QLatin1Char('_');
+
+ enum CurrentContext {
+ ContextUnknown,
+ ContextUninteresting,
+ ContextInteresting
+ };
+
+ QXmlStreamReader xml(&tsFile);
+
+ while (!xml.atEnd() && xml.name() != tsElement)
+ xml.readNextStartElement();
+
+ while (xml.readNextStartElement()) {
+ if (xml.name() == contextElement) {
+ CurrentContext currentContext = ContextUnknown;
+ while (xml.readNextStartElement()) {
+ if (currentContext == ContextUnknown) {
+ // Expect name element before message elements
+ if (xml.name() == nameElement) {
+ QString nameText = xml.readElementText();
+ if (nameText == applicationCaptionsContext || nameText == pkgNameContext) {
+ currentContext = ContextInteresting;
+ } else {
+ currentContext = ContextUninteresting;
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ } else if (currentContext == ContextInteresting) {
+ if (xml.name() == messageElement) {
+ QString source;
+ QString translation;
+ while (xml.readNextStartElement()) {
+ if (xml.name() == sourceElement) {
+ source = xml.readElementText();
+ } else if (xml.name() == translationElement) {
+ translation = xml.readElementText();
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+
+ if (source == shortCaptionId) {
+ if (loc->shortCaption.isEmpty()) {
+ loc->shortCaption = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate application short caption defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == longCaptionId) {
+ if (loc->longCaption.isEmpty()) {
+ loc->longCaption = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate application long caption defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == pkgDisplayNameId) {
+ if (loc->pkgDisplayName.isEmpty()) {
+ loc->pkgDisplayName = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate package display name defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ } else if (source == installerPkgDisplayNameId) {
+ if (loc->installerPkgDisplayName.isEmpty()) {
+ loc->installerPkgDisplayName = translation;
+ } else {
+ fprintf(stderr, "Warning: Duplicate smart installer package display name defined in (%s).\n",
+ qPrintable(tsFilename));
+ }
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+ if (xml.hasError()) {
+ retval = false;
+ fprintf(stderr, "ERROR: Encountered error \"%s\" when parsing ts file (%s).\n",
+ qPrintable(xml.errorString()), qPrintable(tsFilename));
+ }
+ } else {
+ retval = false;
+ fprintf(stderr, "Warning: Could not open ts file (%s).\n", qPrintable(tsFilename));
+ }
+ } else {
+ retval = false;
+ fprintf(stderr, "Warning: ts file does not exist: (%s), unable to parse it.\n",
+ qPrintable(tsFilename));
+ }
+
+ return retval;
+}
+
+QString SymbianCommonGenerator::generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList,
+ const QString &defaultName,
+ bool isForSmartInstaller)
+{
+ QStringList allNames;
+ QString noTranslation = defaultName;
+
+ if (isForSmartInstaller)
+ noTranslation += QLatin1String(" installer");
+
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ QString currentName;
+ if (isForSmartInstaller) {
+ currentName = loc.installerPkgDisplayName;
+ } else {
+ currentName = loc.pkgDisplayName;
+ }
+
+ if (currentName.isEmpty())
+ currentName = noTranslation;
+
+ allNames << currentName;
+ }
+
+ if (!allNames.size())
+ allNames << noTranslation;
+
+ return allNames.join("\",\"");
+
+}
+
+void SymbianCommonGenerator::addLocalizedResourcesToDeployment(const QString &deploymentFilesVar,
+ const SymbianLocalizationList &symbianLocalizationList)
+{
+ QStringList locResources;
+ foreach (QString defaultResource, generator->project->values(deploymentFilesVar)) {
+ if (defaultResource.endsWith(".rsc")) {
+ defaultResource.chop(2);
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ locResources << QString(defaultResource + loc.symbianLanguageCode);
+ }
+ }
+ }
+ generator->project->values(deploymentFilesVar) << locResources;
+}
+
+QString SymbianCommonGenerator::generateLocFileName()
+{
+ QString fileName(fixedTarget);
+ if (!Option::output_dir.isEmpty())
+ fileName = Option::output_dir + QLatin1Char('/') + fileName;
+ fileName.append(".loc");
+ return fileName;
+}
diff --git a/qmake/generators/symbian/symbiancommon.h b/qmake/generators/symbian/symbiancommon.h
new file mode 100644
index 0000000000..5182021439
--- /dev/null
+++ b/qmake/generators/symbian/symbiancommon.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANCOMMON_H
+#define SYMBIANCOMMON_H
+
+#include <project.h>
+#include <makefile.h>
+#include "initprojectdeploy_symbian.h"
+
+#define PRINT_FILE_CREATE_ERROR(filename) fprintf(stderr, "Error: Could not create '%s'\n", qPrintable(filename));
+
+class SymbianLocalization
+{
+public:
+ QString qtLanguageCode;
+ QString symbianLanguageCode;
+ QString shortCaption;
+ QString longCaption;
+ QString pkgDisplayName;
+ QString installerPkgDisplayName;
+};
+
+typedef QList<SymbianLocalization> SymbianLocalizationList;
+typedef QListIterator<SymbianLocalization> SymbianLocalizationListIterator;
+
+class SymbianCommonGenerator
+{
+public:
+ enum TargetType {
+ TypeExe,
+ TypeDll,
+ TypeLib,
+ TypePlugin,
+ TypeSubdirs
+ };
+
+
+ SymbianCommonGenerator(MakefileGenerator *generator);
+
+ virtual void init();
+
+protected:
+
+ QString removePathSeparators(QString &file);
+ void removeSpecialCharacters(QString& str);
+ void generatePkgFile(const QString &iconFile,
+ bool epocBuild,
+ const SymbianLocalizationList &symbianLocalizationList);
+ bool containsStartWithItem(const QChar &c, const QStringList& src);
+
+ void writeRegRssFile(QMap<QString, QStringList> &useritems);
+ void writeRegRssList(QTextStream &t, QStringList &userList,
+ const QString &listTag,
+ const QString &listItem);
+ void writeRssFile(QString &numberOfIcons, QString &iconfile);
+ void writeLocFile(const SymbianLocalizationList &symbianLocalizationList);
+ void readRssRules(QString &numberOfIcons,
+ QString &iconFile,
+ QMap<QString, QStringList> &userRssRules);
+
+ void writeCustomDefFile();
+
+ void parseTsFiles(SymbianLocalizationList *symbianLocalizationList);
+ void fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList);
+
+ void parsePreRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList,
+ QStringList *languageRuleList,
+ QStringList *headerRuleList,
+ QStringList *vendorRuleList);
+ void parsePostRules(const QString &deploymentVariable,
+ const QString &variableSuffix,
+ QStringList *rawRuleList);
+ bool parseTsContent(const QString &tsFilename, SymbianLocalization *loc);
+ QString generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList,
+ const QString &defaultName,
+ bool isForSmartInstaller);
+ void addLocalizedResourcesToDeployment(const QString &deploymentFilesVar,
+ const SymbianLocalizationList &symbianLocalizationList);
+ QString generateLocFileName();
+
+protected:
+ MakefileGenerator *generator;
+
+ QStringList generatedFiles;
+ QStringList generatedDirs;
+ QString fixedTarget;
+ QString privateDirUid;
+ QString uid3;
+ TargetType targetType;
+};
+
+#endif // SYMBIANCOMMON_H
diff --git a/qmake/generators/symbian/symmake.cpp b/qmake/generators/symbian/symmake.cpp
new file mode 100644
index 0000000000..08d33700f7
--- /dev/null
+++ b/qmake/generators/symbian/symmake.cpp
@@ -0,0 +1,1136 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symmake.h"
+
+#include <qstring.h>
+#include <qhash.h>
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <stdlib.h>
+#include <qdebug.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+#define RESOURCE_DIRECTORY_MMP "/resource/apps"
+#define REGISTRATION_RESOURCE_DIRECTORY_HW "/private/10003a3f/import/apps"
+#define PLUGIN_COMMON_DEF_FILE_FOR_MMP "./plugin_common.def"
+#define BLD_INF_FILENAME_LEN (sizeof(BLD_INF_FILENAME) - 1)
+
+#define BLD_INF_RULES_BASE "BLD_INF_RULES."
+#define BLD_INF_TAG_PLATFORMS "prj_platforms"
+#define BLD_INF_TAG_MMPFILES "prj_mmpfiles"
+#define BLD_INF_TAG_TESTMMPFILES "prj_testmmpfiles"
+#define BLD_INF_TAG_EXTENSIONS "prj_extensions"
+#define BLD_INF_TAG_TESTEXTENSIONS "prj_testextensions"
+
+#define MMP_TARGET "TARGET"
+#define MMP_TARGETTYPE "TARGETTYPE"
+#define MMP_SECUREID "SECUREID"
+#define MMP_OPTION "OPTION"
+#define MMP_LINKEROPTION "LINKEROPTION"
+#define MMP_CAPABILITY "CAPABILITY"
+#define MMP_EPOCALLOWDLLDATA "EPOCALLOWDLLDATA"
+#define MMP_EPOCHEAPSIZE "EPOCHEAPSIZE"
+#define MMP_EPOCSTACKSIZE "EPOCSTACKSIZE"
+#define MMP_UID "UID"
+#define MMP_VENDORID "VENDORID"
+#define MMP_VERSION "VERSION"
+#define MMP_START_RESOURCE "START RESOURCE"
+#define MMP_END_RESOURCE "END"
+
+#define VAR_CXXFLAGS "QMAKE_CXXFLAGS"
+#define VAR_CFLAGS "QMAKE_CFLAGS"
+#define VAR_LFLAGS "QMAKE_LFLAGS"
+
+#define DEFINE_REPLACE_REGEXP "[^A-Z0-9_]"
+
+QString SymbianMakefileGenerator::fixPathForMmp(const QString& origPath, const QDir& parentDir)
+{
+ static QString epocRootStr;
+ if (epocRootStr.isEmpty()) {
+ epocRootStr = qt_epocRoot();
+ QFileInfo efi(epocRootStr);
+ if (!efi.exists() || epocRootStr.isEmpty()) {
+ fprintf(stderr, "Unable to resolve epocRoot '%s' to real dir on current drive, defaulting to '/' for mmp paths\n", qPrintable(qt_epocRoot()));
+ epocRootStr = "/";
+ } else {
+ epocRootStr = efi.absoluteFilePath();
+ }
+ if (!epocRootStr.endsWith("/"))
+ epocRootStr += "/";
+
+ epocRootStr += "epoc32/";
+ }
+
+ QString resultPath = origPath;
+
+ // Make it relative, unless it starts with "%epocroot%/epoc32/"
+ if (resultPath.startsWith(epocRootStr, Qt::CaseInsensitive)) {
+ resultPath.replace(epocRootStr, "/epoc32/", Qt::CaseInsensitive);
+ } else {
+ resultPath = parentDir.relativeFilePath(resultPath);
+ }
+ resultPath = QDir::cleanPath(resultPath);
+
+ if (resultPath.isEmpty())
+ resultPath = ".";
+
+ return resultPath;
+}
+
+QString SymbianMakefileGenerator::absolutizePath(const QString& origPath)
+{
+ // Prepend epocroot to any paths beginning with "/epoc32/"
+ QString resultPath = QDir::fromNativeSeparators(origPath);
+ if (resultPath.startsWith("/epoc32/", Qt::CaseInsensitive))
+ resultPath = QDir::fromNativeSeparators(qt_epocRoot()) + resultPath.mid(1);
+
+ QFileInfo fi(fileInfo(resultPath));
+
+ // Since origPath can be something given in HEADERS, we need to check if we are dealing
+ // with a file or a directory. In case the origPath doesn't yet exist, isFile() returns
+ // false and we default to assuming it is a dir.
+ if (fi.isFile()) {
+ resultPath = fi.absolutePath();
+ } else {
+ resultPath = fi.absoluteFilePath();
+ }
+
+ resultPath = QDir::cleanPath(resultPath);
+
+ return resultPath;
+}
+
+SymbianMakefileGenerator::SymbianMakefileGenerator() : MakefileGenerator(), SymbianCommonGenerator(this) { }
+SymbianMakefileGenerator::~SymbianMakefileGenerator() { }
+
+void SymbianMakefileGenerator::writeHeader(QTextStream &t)
+{
+ t << "// ============================================================================" << endl;
+ t << "// * Makefile for building: " << escapeFilePath(var("TARGET")) << endl;
+ t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// * This file is generated by qmake and should not be modified by the" << endl;
+ t << "// * user." << endl;
+ t << "// * Project: " << fileFixify(project->projectFile()) << endl;
+ t << "// * Template: " << var("TEMPLATE") << endl;
+ t << "// ============================================================================" << endl;
+ t << endl;
+
+ // Defining define for bld.inf
+
+ QString shortProFilename = project->projectFile();
+ shortProFilename.replace(0, shortProFilename.lastIndexOf("/") + 1, QString(""));
+ shortProFilename.replace(Option::pro_ext, QString(""));
+
+ QString bldinfDefine = shortProFilename;
+ bldinfDefine.append("_");
+ bldinfDefine.append(generate_uid(project->projectFile()));
+ bldinfDefine = bldinfDefine.toUpper();
+
+ // replace anything not alphanumeric with underscore
+ QRegExp replacementMask(DEFINE_REPLACE_REGEXP);
+ bldinfDefine.replace(replacementMask, QLatin1String("_"));
+
+ bldinfDefine.prepend("BLD_INF_");
+
+ t << "#define " << bldinfDefine << endl << endl;
+}
+
+bool SymbianMakefileGenerator::writeMakefile(QTextStream &t)
+{
+ if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
+ fprintf(stderr, "Project files not generated because all requirements are not met:\n\t%s\n",
+ qPrintable(var("QMAKE_FAILED_REQUIREMENTS")));
+ return false;
+ }
+
+ writeHeader(t);
+
+ QString numberOfIcons;
+ QString iconFile;
+ QMap<QString, QStringList> userRssRules;
+ readRssRules(numberOfIcons, iconFile, userRssRules);
+
+ SymbianLocalizationList symbianLocalizationList;
+ parseTsFiles(&symbianLocalizationList);
+
+ // Generate pkg files if there are any actual files to deploy
+ bool generatePkg = false;
+
+ if (targetType == TypeExe) {
+ generatePkg = true;
+ } else {
+ foreach(QString item, project->values("DEPLOYMENT")) {
+ // ### Qt 5: remove .sources, inconsistent with INSTALLS
+ if (!project->values(item + ".sources").isEmpty() ||
+ !project->values(item + ".files").isEmpty()) {
+ generatePkg = true;
+ break;
+ }
+ }
+ }
+
+ if (generatePkg) {
+ generatePkgFile(iconFile, true, symbianLocalizationList);
+ }
+
+ writeBldInfContent(t, generatePkg, iconFile);
+
+ // Generate empty wrapper makefile here, because wrapper makefile must exist before writeMkFile,
+ // but all required data is not yet available.
+ bool isPrimaryMakefile = true;
+ QString wrapperFileName = Option::output_dir + QLatin1Char('/') + QLatin1String("Makefile");
+ QString outputFileName = fileInfo(Option::output.fileName()).fileName();
+ if (outputFileName != BLD_INF_FILENAME) {
+ wrapperFileName.append(".").append(outputFileName.startsWith(BLD_INF_FILENAME)
+ ? outputFileName.mid(sizeof(BLD_INF_FILENAME))
+ : outputFileName);
+ isPrimaryMakefile = false;
+ }
+
+ QFile wrapperMakefile(wrapperFileName);
+ if (wrapperMakefile.open(QIODevice::WriteOnly)) {
+ generatedFiles << wrapperFileName;
+ } else {
+ PRINT_FILE_CREATE_ERROR(wrapperFileName);
+ return false;
+ }
+
+ if (targetType == TypeSubdirs) {
+ // If we have something to deploy, generate extension makefile for just that, since
+ // normal extension makefile is not getting generated and we need emulator deployment to be done.
+ if (generatePkg)
+ writeMkFile(wrapperFileName, true);
+ writeWrapperMakefile(wrapperMakefile, isPrimaryMakefile);
+ return true;
+ }
+
+ writeMkFile(wrapperFileName, false);
+
+ QString absoluteMmpFileName = Option::output_dir + QLatin1Char('/') + mmpFileName;
+ writeMmpFile(absoluteMmpFileName, symbianLocalizationList);
+
+ if (targetType == TypeExe) {
+ if (!project->isActiveConfig("no_icon")) {
+ writeRegRssFile(userRssRules);
+ writeRssFile(numberOfIcons, iconFile);
+ writeLocFile(symbianLocalizationList);
+ }
+ }
+
+ writeCustomDefFile();
+ writeWrapperMakefile(wrapperMakefile, isPrimaryMakefile);
+
+ return true;
+}
+
+void SymbianMakefileGenerator::init()
+{
+ MakefileGenerator::init();
+ SymbianCommonGenerator::init();
+
+ if (0 != project->values("QMAKE_PLATFORM").size())
+ platform = varGlue("QMAKE_PLATFORM", "", " ", "");
+
+ if (0 == project->values("QMAKESPEC").size())
+ project->values("QMAKESPEC").append(qgetenv("QMAKESPEC"));
+
+ project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS"));
+ project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE"));
+
+ // Disallow renaming of bld.inf.
+ project->values("MAKEFILE").clear();
+ project->values("MAKEFILE") += BLD_INF_FILENAME;
+
+ // .mmp
+ mmpFileName = fixedTarget;
+ if (targetType == TypeExe)
+ mmpFileName.append("_exe");
+ else if (targetType == TypeDll || targetType == TypePlugin)
+ mmpFileName.append("_dll");
+ else if (targetType == TypeLib)
+ mmpFileName.append("_lib");
+ mmpFileName.append(Option::mmp_ext);
+
+ initMmpVariables();
+
+ uid2 = project->first("TARGET.UID2");
+
+ uid2 = uid2.trimmed();
+}
+
+QString SymbianMakefileGenerator::getTargetExtension()
+{
+ QString ret;
+ if (targetType == TypeExe) {
+ ret.append("exe");
+ } else if (targetType == TypeLib) {
+ ret.append("lib");
+ } else if (targetType == TypeDll || targetType == TypePlugin) {
+ ret.append("dll");
+ } else if (targetType == TypeSubdirs) {
+ // Not actually usable, so return empty
+ } else {
+ // If nothing else set, default to exe
+ ret.append("exe");
+ }
+
+ return ret;
+}
+
+QString SymbianMakefileGenerator::generateUID3()
+{
+ QString target = project->first("TARGET");
+ QString currPath = qmake_getpwd();
+ target.prepend("/").prepend(currPath);
+ return generate_test_uid(target);
+}
+
+void SymbianMakefileGenerator::initMmpVariables()
+{
+ QStringList sysincspaths;
+ QStringList srcincpaths;
+ QStringList srcpaths;
+
+ srcpaths << project->values("SOURCES") << project->values("GENERATED_SOURCES");
+ srcpaths << project->values("UNUSED_SOURCES") << project->values("UI_SOURCES_DIR");
+ srcpaths << project->values("UI_DIR");
+
+ QDir current = QDir::current();
+ QString absolutizedCurrent = absolutizePath(".");
+
+ for (int j = 0; j < srcpaths.size(); ++j) {
+ QFileInfo fi(fileInfo(srcpaths.at(j)));
+ // Sometimes sources have other than *.c* files (e.g. *.moc); prune them.
+ if (fi.suffix().startsWith("c")) {
+ if (fi.filePath().length() > fi.fileName().length()) {
+ appendIfnotExist(srcincpaths, fi.path());
+ sources[absolutizePath(fi.path())] += fi.fileName();
+ } else {
+ sources[absolutizedCurrent] += fi.fileName();
+ appendIfnotExist(srcincpaths, absolutizedCurrent);
+ }
+ }
+ }
+
+ QStringList incpaths;
+ incpaths << project->values("INCLUDEPATH");
+ incpaths << QLibraryInfo::location(QLibraryInfo::HeadersPath);
+ incpaths << project->values("HEADERS");
+ incpaths << srcincpaths;
+ incpaths << project->values("UI_HEADERS_DIR");
+ incpaths << project->values("UI_DIR");
+
+ for (int j = 0; j < incpaths.size(); ++j) {
+ QString includepath = absolutizePath(incpaths.at(j));
+ appendIfnotExist(sysincspaths, includepath);
+ appendAbldTempDirs(sysincspaths, includepath);
+ }
+
+ // Remove duplicate include path entries
+ QStringList temporary;
+ for (int i = 0; i < sysincspaths.size(); ++i) {
+ QString origPath = sysincspaths.at(i);
+ QFileInfo origPathInfo(fileInfo(origPath));
+ bool bFound = false;
+
+ for (int j = 0; j < temporary.size(); ++j) {
+ QString tmpPath = temporary.at(j);
+ QFileInfo tmpPathInfo(fileInfo(tmpPath));
+
+ if (origPathInfo.absoluteFilePath() == tmpPathInfo.absoluteFilePath()) {
+ bFound = true;
+ if (!tmpPathInfo.isRelative() && origPathInfo.isRelative()) {
+ // We keep the relative notation
+ temporary.removeOne(tmpPath);
+ temporary << origPath;
+ }
+ }
+ }
+
+ if (!bFound)
+ temporary << origPath;
+
+ }
+
+ sysincspaths.clear();
+ sysincspaths << temporary;
+
+ systeminclude.insert("SYSTEMINCLUDE", sysincspaths);
+
+ // Check MMP_RULES for singleton keywords that are overridden
+ QStringList overridableMmpKeywords;
+ QStringList restrictableMmpKeywords;
+ QStringList restrictedMmpKeywords;
+ bool inResourceBlock = false;
+
+ overridableMmpKeywords << QLatin1String(MMP_TARGETTYPE) << QLatin1String(MMP_EPOCHEAPSIZE);
+ restrictableMmpKeywords << QLatin1String(MMP_TARGET) << QLatin1String(MMP_SECUREID)
+ << QLatin1String(MMP_OPTION) << QLatin1String(MMP_LINKEROPTION)
+ << QLatin1String(MMP_CAPABILITY) << QLatin1String(MMP_EPOCALLOWDLLDATA)
+ << QLatin1String(MMP_EPOCSTACKSIZE) << QLatin1String(MMP_UID)
+ << QLatin1String(MMP_VENDORID) << QLatin1String(MMP_VERSION);
+
+ foreach (QString item, project->values("MMP_RULES")) {
+ if (project->values(item).isEmpty()) {
+ handleMmpRulesOverrides(item, inResourceBlock, restrictedMmpKeywords,
+ restrictableMmpKeywords, overridableMmpKeywords);
+ } else {
+ foreach (QString itemRow, project->values(item)) {
+ handleMmpRulesOverrides(itemRow, inResourceBlock, restrictedMmpKeywords,
+ restrictableMmpKeywords, overridableMmpKeywords);
+ }
+ }
+ }
+
+ if (restrictedMmpKeywords.size()) {
+ fprintf(stderr, "Warning: Restricted statements detected in MMP_RULES:\n"
+ " (%s)\n"
+ " Use corresponding qmake variable(s) instead.\n",
+ qPrintable(restrictedMmpKeywords.join(", ")));
+ }
+}
+
+void SymbianMakefileGenerator::handleMmpRulesOverrides(QString &checkString,
+ bool &inResourceBlock,
+ QStringList &restrictedMmpKeywords,
+ const QStringList &restrictableMmpKeywords,
+ const QStringList &overridableMmpKeywords)
+{
+ QString simplifiedString = checkString.simplified();
+
+ if (!inResourceBlock && simplifiedString.startsWith(MMP_START_RESOURCE, Qt::CaseInsensitive))
+ inResourceBlock = true;
+ else if (inResourceBlock && simplifiedString.startsWith(MMP_END_RESOURCE, Qt::CaseInsensitive))
+ inResourceBlock = false;
+
+ // Allow restricted and overridable items in RESOURCE blocks as those do not actually
+ // override anything.
+ if (!inResourceBlock) {
+ appendKeywordIfMatchFound(overriddenMmpKeywords, overridableMmpKeywords, simplifiedString);
+ appendKeywordIfMatchFound(restrictedMmpKeywords, restrictableMmpKeywords, simplifiedString);
+ }
+}
+
+void SymbianMakefileGenerator::appendKeywordIfMatchFound(QStringList &list,
+ const QStringList &keywordList,
+ QString &checkString)
+{
+ // Check if checkString starts with any supplied keyword and
+ // add the found keyword to list if it does.
+ foreach (QString item, keywordList) {
+ if (checkString.startsWith(QString(item).append(" "), Qt::CaseInsensitive)
+ || checkString.compare(item, Qt::CaseInsensitive) == 0) {
+ appendIfnotExist(list, item);
+ }
+ }
+}
+
+
+bool SymbianMakefileGenerator::removeDuplicatedStrings(QStringList &stringList)
+{
+ QStringList tmpStringList;
+
+ for (int i = 0; i < stringList.size(); ++i) {
+ QString string = stringList.at(i);
+ if (tmpStringList.contains(string))
+ continue;
+ else
+ tmpStringList.append(string);
+ }
+
+ stringList.clear();
+ stringList = tmpStringList;
+ return true;
+}
+
+void SymbianMakefileGenerator::writeMmpFileHeader(QTextStream &t)
+{
+ t << "// ==============================================================================" << endl;
+ t << "// Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: ";
+ t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl;
+ t << "// This file is generated by qmake and should not be modified by the" << endl;
+ t << "// user." << endl;
+ t << "// Name : " << mmpFileName << endl;
+ t << "// ==============================================================================" << endl << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFile(QString &filename, const SymbianLocalizationList &symbianLocalizationList)
+{
+ QFile ft(filename);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+
+ QTextStream t(&ft);
+
+ writeMmpFileHeader(t);
+
+ writeMmpFileTargetPart(t);
+
+ writeMmpFileResourcePart(t, symbianLocalizationList);
+
+ writeMmpFileMacrosPart(t);
+
+ writeMmpFileIncludePart(t);
+
+ QDir current = QDir::current();
+
+ for (QMap<QString, QStringList>::iterator it = sources.begin(); it != sources.end(); ++it) {
+ QStringList values = it.value();
+ QString currentSourcePath = it.key();
+
+ if (values.size())
+ t << "SOURCEPATH \t" << fixPathForMmp(currentSourcePath, current) << endl;
+
+ for (int i = 0; i < values.size(); ++i) {
+ QString sourceFileName = values.at(i);
+ t << "SOURCE\t\t" << sourceFileName << endl;
+ }
+ t << endl;
+ }
+ t << endl;
+
+ if (!project->isActiveConfig("static") && !project->isActiveConfig("staticlib")) {
+ writeMmpFileLibraryPart(t);
+ }
+
+ writeMmpFileCapabilityPart(t);
+
+ writeMmpFileCompilerOptionPart(t);
+
+ writeMmpFileBinaryVersionPart(t);
+
+ writeMmpFileRulesPart(t);
+ } else {
+ PRINT_FILE_CREATE_ERROR(filename)
+ }
+}
+
+void SymbianMakefileGenerator::writeMmpFileMacrosPart(QTextStream& t)
+{
+ t << endl;
+
+ QStringList &defines = project->values("DEFINES");
+ if (defines.size())
+ t << "// Qt Macros" << endl;
+ for (int i = 0; i < defines.size(); ++i) {
+ QString def = defines.at(i);
+ addMacro(t, def);
+ }
+
+ // These are required in order that all methods will be correctly exported e.g from qtestlib
+ QStringList &exp_defines = project->values("PRL_EXPORT_DEFINES");
+ if (exp_defines.size())
+ t << endl << "// Qt Export Defines" << endl;
+ for (int i = 0; i < exp_defines.size(); ++i) {
+ QString def = exp_defines.at(i);
+ addMacro(t, def);
+ }
+
+ t << endl;
+}
+
+void SymbianMakefileGenerator::addMacro(QTextStream& t, const QString& value)
+{
+ t << "MACRO\t\t" << value << endl;
+}
+
+
+void SymbianMakefileGenerator::writeMmpFileTargetPart(QTextStream& t)
+{
+ bool skipTargetType = overriddenMmpKeywords.contains(MMP_TARGETTYPE);
+ bool skipEpocHeapSize = overriddenMmpKeywords.contains(MMP_EPOCHEAPSIZE);
+
+ if (targetType == TypeExe) {
+ t << MMP_TARGET "\t\t" << fixedTarget << ".exe" << endl;
+ if (!skipTargetType) {
+ if (project->isActiveConfig("stdbinary"))
+ t << MMP_TARGETTYPE "\t\tSTDEXE" << endl;
+ else
+ t << MMP_TARGETTYPE "\t\tEXE" << endl;
+ }
+ } else if (targetType == TypeDll || targetType == TypePlugin) {
+ t << MMP_TARGET "\t\t" << fixedTarget << ".dll" << endl;
+ if (!skipTargetType) {
+ if (project->isActiveConfig("stdbinary"))
+ t << MMP_TARGETTYPE "\t\tSTDDLL" << endl;
+ else
+ t << MMP_TARGETTYPE "\t\tDLL" << endl;
+ }
+ } else if (targetType == TypeLib) {
+ t << MMP_TARGET "\t\t" << fixedTarget << ".lib" << endl;
+ if (!skipTargetType) {
+ if (project->isActiveConfig("stdbinary"))
+ t << MMP_TARGETTYPE "\t\tSTDLIB" << endl;
+ else
+ t << MMP_TARGETTYPE "\t\tLIB" << endl;
+ }
+ } else {
+ fprintf(stderr, "Error: Unexpected targettype (%d) in SymbianMakefileGenerator::writeMmpFileTargetPart\n", targetType);
+ }
+
+ t << endl;
+
+ t << MMP_UID "\t\t" << uid2 << " " << uid3 << endl;
+
+ if (0 != project->values("TARGET.SID").size()) {
+ t << MMP_SECUREID "\t\t" << project->values("TARGET.SID").join(" ") << endl;
+ } else {
+ if (0 == uid3.size())
+ t << MMP_SECUREID "\t\t0" << endl;
+ else
+ t << MMP_SECUREID "\t\t" << uid3 << endl;
+ }
+
+ // default value used from mkspecs is 0
+ if (0 != project->values("TARGET.VID").size()) {
+ t << MMP_VENDORID "\t\t" << project->values("TARGET.VID").join(" ") << endl;
+ }
+
+ t << endl;
+
+ if (0 != project->first("TARGET.EPOCSTACKSIZE").size())
+ t << MMP_EPOCSTACKSIZE "\t\t" << project->first("TARGET.EPOCSTACKSIZE") << endl;
+ if (!skipEpocHeapSize && 0 != project->values("TARGET.EPOCHEAPSIZE").size())
+ t << MMP_EPOCHEAPSIZE "\t\t" << project->values("TARGET.EPOCHEAPSIZE").join(" ") << endl;
+ if (0 != project->values("TARGET.EPOCALLOWDLLDATA").size())
+ t << MMP_EPOCALLOWDLLDATA << endl;
+
+ if (targetType == TypePlugin && !project->isActiveConfig("stdbinary")) {
+ // Use custom def file for Qt plugins
+ t << "DEFFILE " PLUGIN_COMMON_DEF_FILE_FOR_MMP << endl;
+ }
+
+ t << endl;
+}
+
+
+/*
+ Application registration resource files should be installed to the
+ \private\10003a3f\import\apps directory.
+*/
+void SymbianMakefileGenerator::writeMmpFileResourcePart(QTextStream& t, const SymbianLocalizationList &symbianLocalizationList)
+{
+ if ((targetType == TypeExe) &&
+ !project->isActiveConfig("no_icon")) {
+
+ QString locTarget = fixedTarget;
+ locTarget.append(".rss");
+
+ t << "SOURCEPATH\t\t\t. " << endl;
+ t << "LANG SC "; // no endl
+ SymbianLocalizationListIterator iter(symbianLocalizationList);
+ while (iter.hasNext()) {
+ const SymbianLocalization &loc = iter.next();
+ t << loc.symbianLanguageCode << " "; // no endl
+ }
+ t << endl;
+ t << MMP_START_RESOURCE "\t\t" << locTarget << endl;
+ t << "HEADER" << endl;
+ t << "TARGETPATH\t\t\t" RESOURCE_DIRECTORY_MMP << endl;
+ t << MMP_END_RESOURCE << endl << endl;
+
+ QString regTarget = fixedTarget;
+ regTarget.append("_reg.rss");
+
+ t << "SOURCEPATH\t\t\t." << endl;
+ t << MMP_START_RESOURCE "\t\t" << regTarget << endl;
+ if (isForSymbianSbsv2())
+ t << "DEPENDS " << fixedTarget << ".rsg" << endl;
+ t << "TARGETPATH\t\t" REGISTRATION_RESOURCE_DIRECTORY_HW << endl;
+ t << MMP_END_RESOURCE << endl << endl;
+ }
+}
+
+void SymbianMakefileGenerator::writeMmpFileSystemIncludePart(QTextStream& t)
+{
+ QDir current = QDir::current();
+
+ for (QMap<QString, QStringList>::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) {
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ QString handledPath = values.at(i);
+ t << "SYSTEMINCLUDE\t\t" << fixPathForMmp(handledPath, current) << endl;
+ }
+ }
+
+ t << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFileIncludePart(QTextStream& t)
+{
+ writeMmpFileSystemIncludePart(t);
+}
+
+void SymbianMakefileGenerator::writeMmpFileLibraryPart(QTextStream& t)
+{
+ QStringList &libs = project->values("LIBS");
+ libs << project->values("QMAKE_LIBS") << project->values("QMAKE_LIBS_PRIVATE");
+
+ removeDuplicatedStrings(libs);
+
+ for (int i = 0; i < libs.size(); ++i) {
+ QString lib = libs.at(i);
+ // The -L flag is uninteresting, since all symbian libraries exist in the same directory.
+ if (lib.startsWith("-l")) {
+ lib.remove(0, 2);
+ QString mmpStatement;
+ if (lib.endsWith(".lib")) {
+ lib.chop(4);
+ mmpStatement = "STATICLIBRARY\t";
+ } else {
+ if (lib.endsWith(".dll"))
+ lib.chop(4);
+ mmpStatement = "LIBRARY\t\t";
+ }
+ t << mmpStatement << lib << ".lib" << endl;
+ }
+ }
+
+ t << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFileCapabilityPart(QTextStream& t)
+{
+ if (0 != project->first("TARGET.CAPABILITY").size()) {
+ QStringList &capabilities = project->values("TARGET.CAPABILITY");
+ t << MMP_CAPABILITY "\t\t";
+
+ for (int i = 0; i < capabilities.size(); ++i) {
+ QString cap = capabilities.at(i);
+ t << cap << " ";
+ }
+ } else {
+ t << MMP_CAPABILITY "\t\tNone";
+ }
+ t << endl << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFileConditionalOptions(QTextStream& t,
+ const QString &optionType,
+ const QString &optionTag,
+ const QString &variableBase)
+{
+ foreach(QString compilerVersion, project->values("VERSION_FLAGS." + optionTag)) {
+ QStringList currentValues = project->values(variableBase + "." + compilerVersion);
+ if (currentValues.size()) {
+ t << "#if defined(" << compilerVersion << ")" << endl;
+ t << optionType << " " << optionTag << " " << currentValues.join(" ") << endl;
+ t << "#endif" << endl;
+ }
+ }
+}
+
+void SymbianMakefileGenerator::writeMmpFileSimpleOption(QTextStream& t,
+ const QString &optionType,
+ const QString &optionTag,
+ const QString &options)
+{
+ QString trimmedOptions = options.trimmed();
+ if (!trimmedOptions.isEmpty())
+ t << optionType << " " << optionTag << " " << trimmedOptions << endl;
+}
+
+void SymbianMakefileGenerator::appendMmpFileOptions(QString &options, const QStringList &list)
+{
+ if (list.size()) {
+ options.append(list.join(" "));
+ options.append(" ");
+ }
+}
+
+void SymbianMakefileGenerator::writeMmpFileCompilerOptionPart(QTextStream& t)
+{
+ QStringList keywords = project->values("MMP_OPTION_KEYWORDS");
+ QStringList commonCxxFlags = project->values(VAR_CXXFLAGS);
+ QStringList commonCFlags = project->values(VAR_CFLAGS);
+ QStringList commonLFlags = project->values(VAR_LFLAGS);
+
+ foreach(QString item, keywords) {
+ QString compilerOption;
+ QString linkerOption;
+
+ appendMmpFileOptions(compilerOption, project->values(VAR_CXXFLAGS "." + item));
+ appendMmpFileOptions(compilerOption, project->values(VAR_CFLAGS "." + item));
+ appendMmpFileOptions(compilerOption, commonCxxFlags);
+ appendMmpFileOptions(compilerOption, commonCFlags);
+
+ appendMmpFileOptions(linkerOption, project->values(VAR_LFLAGS "." + item));
+ appendMmpFileOptions(linkerOption, commonLFlags);
+
+ writeMmpFileSimpleOption(t, MMP_OPTION, item, compilerOption);
+ writeMmpFileSimpleOption(t, MMP_LINKEROPTION, item, linkerOption);
+
+ writeMmpFileConditionalOptions(t, MMP_OPTION, item, VAR_CXXFLAGS);
+ writeMmpFileConditionalOptions(t, MMP_LINKEROPTION, item, VAR_LFLAGS);
+ }
+
+ t << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFileBinaryVersionPart(QTextStream& t)
+{
+ QString applicationVersion = project->first("VERSION");
+ QStringList verNumList = applicationVersion.split('.');
+ uint major = 0;
+ uint minor = 0;
+ uint patch = 0;
+ bool success = false;
+
+ if (verNumList.size() > 0) {
+ major = verNumList[0].toUInt(&success);
+ if (success && verNumList.size() > 1) {
+ minor = verNumList[1].toUInt(&success);
+ if (success && verNumList.size() > 2) {
+ patch = verNumList[2].toUInt(&success);
+ }
+ }
+ }
+
+ QString mmpVersion;
+ if (success && major <= 0xFFFF && minor <= 0xFF && patch <= 0xFF) {
+ // Symbian binary version only has major and minor components, so compress
+ // Qt's minor and patch values into the minor component. Since Symbian's minor
+ // component is a 16 bit value, only allow 8 bits for each to avoid overflow.
+ mmpVersion.append(QString::number(major))
+ .append('.')
+ .append(QString::number((minor << 8) + patch));
+ } else {
+ if (!applicationVersion.isEmpty())
+ fprintf(stderr, "Invalid VERSION string: %s\n", qPrintable(applicationVersion));
+ mmpVersion = "10.0"; // Default binary version for symbian is 10.0
+ }
+
+ t << MMP_VERSION " " << mmpVersion << endl;
+}
+
+void SymbianMakefileGenerator::writeMmpFileRulesPart(QTextStream& t)
+{
+ foreach(QString item, project->values("MMP_RULES")) {
+ t << endl;
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line mmp statements
+ if (project->values(item).isEmpty()) {
+ t << item << endl;
+ } else {
+ foreach(QString itemRow, project->values(item)) {
+ t << itemRow << endl;
+ }
+ }
+ }
+}
+
+void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploymentExtension, const QString &iconFile)
+{
+ // Read user defined bld inf rules
+
+ QMap<QString, QStringList> userBldInfRules;
+ for (QMap<QString, QStringList>::iterator it = project->variables().begin(); it != project->variables().end(); ++it) {
+ if (it.key().startsWith(BLD_INF_RULES_BASE)) {
+ QString newKey = it.key().mid(sizeof(BLD_INF_RULES_BASE) - 1);
+ if (newKey.isEmpty()) {
+ fprintf(stderr, "Warning: Empty BLD_INF_RULES key encountered\n");
+ continue;
+ }
+ QStringList newValues;
+ QStringList values = it.value();
+ foreach(QString item, values) {
+ // If there is no stringlist defined for a rule, use rule name directly
+ // This is convenience for defining single line statements
+ if (project->values(item).isEmpty()) {
+ newValues << item;
+ } else {
+ foreach(QString itemRow, project->values(item)) {
+ newValues << itemRow;
+ }
+ }
+ }
+ userBldInfRules.insert(newKey, newValues);
+ }
+ }
+
+ // Add includes of subdirs bld.inf files
+
+ QString currentPath = qmake_getpwd();
+ QDir directory(currentPath);
+
+ const QStringList &subdirs = project->values("SUBDIRS");
+ foreach(QString item, subdirs) {
+ bool fromFile = false;
+ QString fixedItem;
+ if (!project->isEmpty(item + ".file")) {
+ fixedItem = project->first(item + ".file");
+ fromFile = true;
+ } else if (!project->isEmpty(item + ".subdir")) {
+ fixedItem = project->first(item + ".subdir");
+ fromFile = false;
+ } else {
+ fixedItem = item;
+ fromFile = item.endsWith(Option::pro_ext);
+ }
+
+ QString condition;
+ if (!project->isEmpty(item + ".condition"))
+ condition = project->first(item + ".condition");
+
+ QFileInfo subdir(fileInfo(fixedItem));
+ QString relativePath = directory.relativeFilePath(fixedItem);
+ QString fullProName = subdir.absoluteFilePath();
+ QString bldinfFilename;
+ QString subdirFileName;
+
+ if (fromFile) {
+ subdirFileName = subdir.completeBaseName();
+ } else {
+ subdirFileName = subdir.fileName();
+ }
+
+ if (subdir.isDir()) {
+ // Subdir is a regular project
+ bldinfFilename = relativePath + QString("/") + QString(BLD_INF_FILENAME);
+ fullProName += QString("/") + subdirFileName + Option::pro_ext;
+ } else {
+ // Subdir is actually a .pro file
+ if (relativePath.contains("/")) {
+ // .pro not in same directory as parent .pro
+ relativePath.remove(relativePath.lastIndexOf("/") + 1, relativePath.length());
+ bldinfFilename = relativePath;
+ } else {
+ // .pro and parent .pro in same directory
+ bldinfFilename = QString("./");
+ }
+ bldinfFilename += QString(BLD_INF_FILENAME ".") + subdirFileName;
+ }
+
+ QString uid = generate_uid(fullProName);
+ QString bldinfDefine = QString("BLD_INF_") + subdirFileName + QString("_") + uid;
+ bldinfDefine = bldinfDefine.toUpper();
+
+ // replace anything not alphanumeric with underscore
+ QRegExp replacementMask(DEFINE_REPLACE_REGEXP);
+ bldinfDefine.replace(replacementMask, QLatin1String("_"));
+
+ if (!condition.isEmpty())
+ t << "#if defined(" << condition << ")" << endl;
+
+ t << "#ifndef " << bldinfDefine << endl;
+ t << "\t#include \"" << bldinfFilename << "\"" << endl;
+ t << "#endif" << endl;
+
+ if (!condition.isEmpty())
+ t << "#endif" << endl;
+
+ }
+
+ // Add supported project platforms
+
+ t << endl << BLD_INF_TAG_PLATFORMS << endl << endl;
+ if (0 != project->values("SYMBIAN_PLATFORMS").size())
+ t << project->values("SYMBIAN_PLATFORMS").join(" ") << endl;
+
+ QStringList userItems = userBldInfRules.value(BLD_INF_TAG_PLATFORMS);
+ foreach(QString item, userItems)
+ t << item << endl;
+ userBldInfRules.remove(BLD_INF_TAG_PLATFORMS);
+ t << endl;
+
+ // Add project mmps and old style extension makefiles
+
+ QString mmpTag;
+ if (project->isActiveConfig(SYMBIAN_TEST_CONFIG))
+ mmpTag = QLatin1String(BLD_INF_TAG_TESTMMPFILES);
+ else
+ mmpTag = QLatin1String(BLD_INF_TAG_MMPFILES);
+
+ t << endl << mmpTag << endl << endl;
+
+ writeBldInfMkFilePart(t, addDeploymentExtension);
+ if (targetType != TypeSubdirs)
+ t << mmpFileName << endl;
+
+ userItems = userBldInfRules.value(mmpTag);
+ foreach(QString item, userItems)
+ t << item << endl;
+ userBldInfRules.remove(mmpTag);
+
+ QString extensionTag;
+ if (project->isActiveConfig(SYMBIAN_TEST_CONFIG))
+ extensionTag = QLatin1String(BLD_INF_TAG_TESTEXTENSIONS);
+ else
+ extensionTag = QLatin1String(BLD_INF_TAG_EXTENSIONS);
+
+ t << endl << extensionTag << endl << endl;
+
+ // Generate extension rules
+
+ writeBldInfExtensionRulesPart(t, iconFile);
+
+ userItems = userBldInfRules.value(extensionTag);
+ foreach(QString item, userItems)
+ t << item << endl;
+ userBldInfRules.remove(extensionTag);
+
+ // Add rest of the user defined content
+
+ for (QMap<QString, QStringList>::iterator it = userBldInfRules.begin(); it != userBldInfRules.end(); ++it) {
+ t << endl << endl << it.key() << endl << endl;
+ userItems = it.value();
+ foreach(QString item, userItems)
+ t << item << endl;
+ }
+}
+
+void SymbianMakefileGenerator::appendIfnotExist(QStringList &list, QString value)
+{
+ if (!list.contains(value))
+ list += value;
+}
+
+void SymbianMakefileGenerator::appendIfnotExist(QStringList &list, QStringList values)
+{
+ foreach(QString item, values)
+ appendIfnotExist(list, item);
+}
+
+
+QString SymbianMakefileGenerator::removeTrailingPathSeparators(QString &file)
+{
+ QString ret = file;
+ if (ret.endsWith(QDir::separator())) {
+ ret.remove(ret.length() - 1, 1);
+ }
+
+ return ret;
+}
+
+void SymbianMakefileGenerator::generateCleanCommands(QTextStream& t,
+ const QStringList& toClean,
+ const QString& cmd,
+ const QString& cmdOptions,
+ const QString& itemPrefix,
+ const QString& itemSuffix)
+{
+ for (int i = 0; i < toClean.size(); ++i) {
+ QString item = toClean.at(i);
+ item.prepend(itemPrefix).append(itemSuffix);
+#if defined(Q_OS_WIN)
+ t << "\t-@ if EXIST \"" << QDir::toNativeSeparators(item) << "\" ";
+ t << cmd << " " << cmdOptions << " \"" << QDir::toNativeSeparators(item) << "\"" << endl;
+#else
+ t << "\t-if test -e " << QDir::toNativeSeparators(item) << "; then ";
+ t << cmd << " " << cmdOptions << " " << QDir::toNativeSeparators(item) << "; fi" << endl;
+#endif
+ }
+}
+
+void SymbianMakefileGenerator::generateDistcleanTargets(QTextStream& t)
+{
+ t << "dodistclean:" << endl;
+ const QStringList &subdirs = project->values("SUBDIRS");
+ foreach(QString item, subdirs) {
+ bool fromFile = false;
+ QString fixedItem;
+ if (!project->isEmpty(item + ".file")) {
+ fixedItem = project->first(item + ".file");
+ fromFile = true;
+ } else if (!project->isEmpty(item + ".subdir")) {
+ fixedItem = project->first(item + ".subdir");
+ fromFile = false;
+ } else {
+ fromFile = item.endsWith(Option::pro_ext);
+ fixedItem = item;
+ }
+ QFileInfo fi(fileInfo(fixedItem));
+ if (!fromFile) {
+ t << "\t-$(MAKE) -f \"" << Option::fixPathToTargetOS(fi.absoluteFilePath() + "/Makefile") << "\" dodistclean" << endl;
+ } else {
+ QString itemName = fi.fileName();
+ int extIndex = itemName.lastIndexOf(Option::pro_ext);
+ if (extIndex)
+ fixedItem = fi.absolutePath() + "/" + QString("Makefile.") + itemName.mid(0, extIndex);
+ t << "\t-$(MAKE) -f \"" << Option::fixPathToTargetOS(fixedItem) << "\" dodistclean" << endl;
+ }
+
+ }
+
+ generatedFiles << Option::fixPathToTargetOS(fileInfo(Option::output.fileName()).absoluteFilePath()); // bld.inf
+ generatedFiles << project->values("QMAKE_INTERNAL_PRL_FILE"); // Add generated prl files for cleanup
+ generatedFiles << project->values("QMAKE_DISTCLEAN"); // Add any additional files marked for distclean
+ QStringList fixedFiles;
+ QStringList fixedDirs;
+ foreach(QString item, generatedFiles) {
+ QString fixedItem = Option::fixPathToTargetOS(fileInfo(item).absoluteFilePath());
+ if (!fixedFiles.contains(fixedItem)) {
+ fixedFiles << fixedItem;
+ }
+ }
+ foreach(QString item, generatedDirs) {
+ QString fixedItem = Option::fixPathToTargetOS(fileInfo(item).absoluteFilePath());
+ if (!fixedDirs.contains(fixedItem)) {
+ fixedDirs << fixedItem;
+ }
+ }
+ generateCleanCommands(t, fixedFiles, "$(DEL_FILE)", "", "", "");
+ generateCleanCommands(t, fixedDirs, "$(DEL_DIR)", "", "", "");
+ t << endl;
+
+ t << "distclean: clean dodistclean" << endl;
+ t << endl;
+}
+
+// Returns a string that can be used as a dependency to loc file on other targets
+QString SymbianMakefileGenerator::generateLocFileTarget(QTextStream& t, const QString& locCmd)
+{
+ QString locFile;
+ if (targetType == TypeExe && !project->isActiveConfig("no_icon")) {
+ locFile = Option::fixPathToLocalOS(generateLocFileName());
+ t << locFile << QLatin1String(": ") << project->values("SYMBIAN_MATCHED_TRANSLATIONS").join(" ") << endl;
+ t << locCmd << endl;
+ t << endl;
+ locFile += QLatin1Char(' ');
+ }
+
+ return locFile;
+}
diff --git a/qmake/generators/symbian/symmake.h b/qmake/generators/symbian/symmake.h
new file mode 100644
index 0000000000..a38fbe54ed
--- /dev/null
+++ b/qmake/generators/symbian/symmake.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMMAKEFILE_H
+#define SYMMAKEFILE_H
+
+#include "initprojectdeploy_symbian.h"
+#include "symbiancommon.h"
+#include <makefile.h>
+
+QT_BEGIN_NAMESPACE
+
+#define BLD_INF_FILENAME "bld.inf"
+#define MAKEFILE_DEPENDENCY_SEPARATOR " \\\n\t"
+#define QT_EXTRA_INCLUDE_DIR "tmp"
+#define MAKE_CACHE_NAME ".make.cache"
+#define SYMBIAN_TEST_CONFIG "symbian_test"
+
+class SymbianMakefileGenerator : public MakefileGenerator, public SymbianCommonGenerator
+{
+protected:
+ QString platform;
+ QString uid2;
+ QString mmpFileName;
+ QMap<QString, QStringList> sources;
+ QMap<QString, QStringList> systeminclude;
+ QMap<QString, QStringList> library;
+ // (output file) (source , command)
+ QMap<QString, QStringList> makmakeCommands;
+ QStringList overriddenMmpKeywords;
+
+ QString fixPathForMmp(const QString& origPath, const QDir& parentDir);
+ QString absolutizePath(const QString& origPath);
+
+ virtual bool writeMakefile(QTextStream &t);
+
+ virtual void init();
+
+ QString getTargetExtension();
+
+ QString generateUID3();
+
+ void initMmpVariables();
+ void generateMmpFileName();
+ void handleMmpRulesOverrides(QString &checkString,
+ bool &inResourceBlock,
+ QStringList &restrictedMmpKeywords,
+ const QStringList &restrictableMmpKeywords,
+ const QStringList &overridableMmpKeywords);
+ void appendKeywordIfMatchFound(QStringList &list,
+ const QStringList &keywordList,
+ QString &checkString);
+
+ void writeHeader(QTextStream &t);
+ void writeBldInfContent(QTextStream& t,
+ bool addDeploymentExtension,
+ const QString &iconFile);
+
+ static bool removeDuplicatedStrings(QStringList& stringList);
+
+ void writeMmpFileHeader(QTextStream &t);
+ void writeMmpFile(QString &filename, const SymbianLocalizationList &symbianLocalizationList);
+ void writeMmpFileMacrosPart(QTextStream& t);
+ void addMacro(QTextStream& t, const QString& value);
+ void writeMmpFileTargetPart(QTextStream& t);
+ void writeMmpFileResourcePart(QTextStream& t, const SymbianLocalizationList &symbianLocalizationList);
+ void writeMmpFileSystemIncludePart(QTextStream& t);
+ void writeMmpFileIncludePart(QTextStream& t);
+ void writeMmpFileLibraryPart(QTextStream& t);
+ void writeMmpFileCapabilityPart(QTextStream& t);
+ void writeMmpFileConditionalOptions(QTextStream& t,
+ const QString &optionType,
+ const QString &optionTag,
+ const QString &variableBase);
+ void writeMmpFileSimpleOption(QTextStream& t,
+ const QString &optionType,
+ const QString &optionTag,
+ const QString &options);
+ void appendMmpFileOptions(QString &options, const QStringList &list);
+ void writeMmpFileCompilerOptionPart(QTextStream& t);
+ void writeMmpFileBinaryVersionPart(QTextStream& t);
+ void writeMmpFileRulesPart(QTextStream& t);
+
+ void appendIfnotExist(QStringList &list, QString value);
+ void appendIfnotExist(QStringList &list, QStringList values);
+
+ QString removeTrailingPathSeparators(QString &file);
+ void generateCleanCommands(QTextStream& t,
+ const QStringList& toClean,
+ const QString& cmd,
+ const QString& cmdOptions,
+ const QString& itemPrefix,
+ const QString& itemSuffix);
+
+ void generateDistcleanTargets(QTextStream& t);
+ QString generateLocFileTarget(QTextStream& t, const QString& locCmd);
+
+ // Subclass implements
+ virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile) = 0;
+ virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension) = 0;
+ virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly) = 0;
+ virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile) = 0;
+ virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath) = 0;
+
+public:
+
+ SymbianMakefileGenerator();
+ ~SymbianMakefileGenerator();
+};
+
+#endif // SYMMAKEFILE_H
diff --git a/qmake/generators/symbian/symmake_abld.cpp b/qmake/generators/symbian/symmake_abld.cpp
new file mode 100644
index 0000000000..b582257519
--- /dev/null
+++ b/qmake/generators/symbian/symmake_abld.cpp
@@ -0,0 +1,523 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symmake_abld.h"
+#include "initprojectdeploy_symbian.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+#define DO_NOTHING_TARGET "do_nothing"
+#define CREATE_TEMPS_TARGET "create_temps"
+#define EXTENSION_CLEAN "extension_clean"
+#define PRE_TARGETDEPS_TARGET "pre_targetdeps"
+#define COMPILER_CLEAN_TARGET "compiler_clean"
+#define FINALIZE_TARGET "finalize"
+#define GENERATED_SOURCES_TARGET "generated_sources"
+#define ALL_SOURCE_DEPS_TARGET "all_source_deps"
+#define DEPLOYMENT_TARGET "deployment"
+#define DEPLOYMENT_CLEAN_TARGET "deployment_clean"
+#define WINSCW_DEPLOYMENT_TARGET "winscw_deployment"
+#define WINSCW_DEPLOYMENT_CLEAN_TARGET "winscw_deployment_clean"
+#define STORE_BUILD_TARGET "store_build"
+
+SymbianAbldMakefileGenerator::SymbianAbldMakefileGenerator() : SymbianMakefileGenerator() { }
+SymbianAbldMakefileGenerator::~SymbianAbldMakefileGenerator() { }
+
+void SymbianAbldMakefileGenerator::writeMkFile(const QString& wrapperFileName, bool deploymentOnly)
+{
+ QFile ft(gnuMakefileName);
+ if (ft.open(QIODevice::WriteOnly)) {
+ generatedFiles << ft.fileName();
+ QTextStream t(&ft);
+
+ t << "# ==============================================================================" << endl;
+ t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: ";
+ t << QDateTime::currentDateTime().toString() << endl;
+ t << "# This file is generated by qmake and should not be modified by the" << endl;
+ t << "# user." << endl;
+ t << "# Name : " << gnuMakefileName << endl;
+ t << "# Part of : " << project->values("TARGET").join(" ") << endl;
+ t << "# Description : This file is used to call necessary targets on wrapper makefile" << endl;
+ t << "# during normal Symbian build process." << endl;
+ t << "# Version : " << endl;
+ t << "#" << endl;
+ t << "# ==============================================================================" << "\n" << endl;
+
+ t << endl << endl;
+
+ t << "MAKE = make" << endl;
+ t << endl;
+
+ t << "VISUAL_CFG = RELEASE" << endl;
+ t << "ifeq \"$(CFG)\" \"UDEB\"" << endl;
+ t << "VISUAL_CFG = DEBUG" << endl;
+ t << "endif" << endl;
+ t << endl;
+
+ t << DO_NOTHING_TARGET " :" << endl;
+ t << "\t" << "@rem " DO_NOTHING_TARGET << endl << endl;
+
+ QString buildDeps;
+ QString cleanDeps;
+ QString finalDeps;
+ QString cleanDepsWinscw;
+ QString finalDepsWinscw;
+ QStringList wrapperTargets;
+ if (deploymentOnly) {
+ buildDeps.append(STORE_BUILD_TARGET);
+ cleanDeps.append(DEPLOYMENT_CLEAN_TARGET);
+ cleanDepsWinscw.append(WINSCW_DEPLOYMENT_CLEAN_TARGET " " DEPLOYMENT_CLEAN_TARGET);
+ finalDeps.append(DEPLOYMENT_TARGET);
+ finalDepsWinscw.append(WINSCW_DEPLOYMENT_TARGET " " DEPLOYMENT_TARGET);
+ wrapperTargets << WINSCW_DEPLOYMENT_TARGET
+ << WINSCW_DEPLOYMENT_CLEAN_TARGET
+ << DEPLOYMENT_TARGET
+ << DEPLOYMENT_CLEAN_TARGET
+ << STORE_BUILD_TARGET;
+ } else {
+ buildDeps.append(CREATE_TEMPS_TARGET " " PRE_TARGETDEPS_TARGET " " STORE_BUILD_TARGET);
+ cleanDeps.append(EXTENSION_CLEAN " " DEPLOYMENT_CLEAN_TARGET);
+ cleanDepsWinscw.append(EXTENSION_CLEAN " " WINSCW_DEPLOYMENT_CLEAN_TARGET " " DEPLOYMENT_CLEAN_TARGET);
+ finalDeps.append(FINALIZE_TARGET " " DEPLOYMENT_TARGET);
+ finalDepsWinscw.append(FINALIZE_TARGET " " WINSCW_DEPLOYMENT_TARGET " " DEPLOYMENT_TARGET);
+ wrapperTargets << PRE_TARGETDEPS_TARGET
+ << CREATE_TEMPS_TARGET
+ << EXTENSION_CLEAN
+ << FINALIZE_TARGET
+ << WINSCW_DEPLOYMENT_CLEAN_TARGET
+ << WINSCW_DEPLOYMENT_TARGET
+ << DEPLOYMENT_CLEAN_TARGET
+ << DEPLOYMENT_TARGET
+ << STORE_BUILD_TARGET;
+ }
+
+ t << "MAKMAKE: " << buildDeps << endl << endl;
+ t << "LIB: " << buildDeps << endl << endl;
+ t << "BLD: " << buildDeps << endl << endl;
+ t << "ifeq \"$(PLATFORM)\" \"WINSCW\"" << endl;
+ t << "CLEAN: " << cleanDepsWinscw << endl;
+ t << "else" << endl;
+ t << "CLEAN: " << cleanDeps << endl;
+ t << "endif" << endl << endl;
+ t << "CLEANLIB: " DO_NOTHING_TARGET << endl << endl;
+ t << "RESOURCE: " DO_NOTHING_TARGET << endl << endl;
+ t << "FREEZE: " DO_NOTHING_TARGET << endl << endl;
+ t << "SAVESPACE: " DO_NOTHING_TARGET << endl << endl;
+ t << "RELEASABLES: " DO_NOTHING_TARGET << endl << endl;
+ t << "ifeq \"$(PLATFORM)\" \"WINSCW\"" << endl;
+ t << "FINAL: " << finalDepsWinscw << endl;
+ t << "else" << endl;
+ t << "FINAL: " << finalDeps << endl;
+ t << "endif" << endl << endl;
+
+ QString makefile(Option::fixPathToTargetOS(fileInfo(wrapperFileName).canonicalFilePath()));
+ foreach(QString target, wrapperTargets) {
+ t << target << " : " << makefile << endl;
+ t << "\t-$(MAKE) -f \"" << makefile << "\" " << target << " QT_SIS_TARGET=$(VISUAL_CFG)-$(PLATFORM)" << endl << endl;
+ }
+
+ t << endl;
+ } // if(ft.open(QIODevice::WriteOnly))
+}
+
+void SymbianAbldMakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile)
+{
+ QStringList allPlatforms;
+ foreach(QString platform, project->values("SYMBIAN_PLATFORMS")) {
+ allPlatforms << platform.toLower();
+ }
+
+ QStringList debugPlatforms = allPlatforms;
+ QStringList releasePlatforms = allPlatforms;
+ releasePlatforms.removeAll("winscw"); // No release for emulator
+
+ QString testClause;
+ if (project->isActiveConfig(SYMBIAN_TEST_CONFIG))
+ testClause = QLatin1String(" test");
+ else
+ testClause = QLatin1String("");
+
+ QTextStream t(&wrapperFile);
+
+ MakefileGenerator::writeHeader(t);
+
+ t << "MAKEFILE = " << fileInfo(wrapperFile.fileName()).fileName() << endl;
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
+ t << "DEL_TREE = " << var("QMAKE_DEL_TREE") << endl;
+ t << "MOVE = " << var("QMAKE_MOVE") << endl;
+ t << "CHK_DIR_EXISTS = " << var("QMAKE_CHK_DIR_EXISTS") << endl;
+ t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
+#ifdef Q_OS_WIN32
+ t << "XCOPY = xcopy /d /f /h /r /y /i" << endl;
+ t << "ABLD = ABLD.BAT" << endl;
+#elif defined(Q_OS_MAC)
+ t << "XCOPY = cp -R -v" << endl;
+ t << "ABLD = abld" << endl;
+#else
+ t << "XCOPY = cp -R -u -v" << endl;
+ t << "ABLD = abld" << endl;
+#endif
+ t << "DEBUG_PLATFORMS = " << debugPlatforms.join(" ") << endl;
+ t << "RELEASE_PLATFORMS = " << releasePlatforms.join(" ") << endl;
+ t << "MAKE = make" << endl;
+ t << endl;
+ t << "ifeq (WINS,$(findstring WINS, $(PLATFORM)))" << endl;
+ t << "ZDIR=$(EPOCROOT)" << QDir::toNativeSeparators("epoc32/release/$(PLATFORM)/$(CFG)/z") << endl;
+ t << "else" << endl;
+ t << "ZDIR=$(EPOCROOT)" << QDir::toNativeSeparators("epoc32/data/z") << endl;
+ t << "endif" << endl;
+ t << endl;
+ t << "DEFINES" << '\t' << " = "
+ << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ")
+ << varGlue("DEFINES","-D"," -D","") << endl;
+
+ t << "INCPATH" << '\t' << " = ";
+
+ for (QMap<QString, QStringList>::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) {
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ t << " -I\"" << values.at(i) << "\"";
+ }
+ }
+ t << endl;
+ t << "first: default" << endl;
+ if (debugPlatforms.contains("winscw"))
+ t << "default: debug-winscw";
+ else if (debugPlatforms.contains("armv5"))
+ t << "default: debug-armv5";
+ else if (debugPlatforms.size())
+ t << "default: debug-" << debugPlatforms.first();
+ else
+ t << "default: all";
+
+ t << endl;
+ if (!isPrimaryMakefile) {
+ t << "all:" << endl;
+ } else {
+ t << "all: debug release" << endl;
+ t << endl;
+
+ QString qmakeCmd = "\t$(QMAKE) \"" + project->projectFile() + "\" " + buildArgs();
+
+ t << "qmake:" << endl;
+ t << qmakeCmd << endl;
+ t << endl;
+
+ t << BLD_INF_FILENAME ": " << project->projectFile() << endl;
+ t << qmakeCmd << endl;
+ t << endl;
+
+ t << "$(ABLD): " BLD_INF_FILENAME << endl;
+ t << "\tbldmake bldfiles" << endl;
+ t << endl;
+
+ QString locFileDep = generateLocFileTarget(t, qmakeCmd);
+
+ t << "debug: " << locFileDep << "$(ABLD)" << endl;
+ foreach(QString item, debugPlatforms) {
+ t << "\t$(ABLD)" << testClause << " build " << item << " udeb" << endl;
+ }
+ t << endl;
+ t << "release: " << locFileDep << "$(ABLD)" << endl;
+ foreach(QString item, releasePlatforms) {
+ t << "\t$(ABLD)" << testClause << " build " << item << " urel" << endl;
+ }
+ t << endl;
+
+ // For more specific builds, targets are in this form: build-platform, e.g. release-armv5
+ foreach(QString item, debugPlatforms) {
+ t << "debug-" << item << ": " << locFileDep << "$(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " build " << item << " udeb" << endl;
+ }
+
+ foreach(QString item, releasePlatforms) {
+ t << "release-" << item << ": " << locFileDep << "$(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " build " << item << " urel" << endl;
+ }
+
+ t << endl;
+ t << "export: $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " export" << endl;
+ t << endl;
+
+ t << "cleanexport: $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " cleanexport" << endl;
+ t << endl;
+
+ }
+
+ // pre_targetdeps target depends on:
+ // - all targets specified in PRE_TARGETDEPS
+ // - the GENERATED_SOURCES sources (so that they get generated)
+ // - all dependencies of sources targeted for compilation
+ // (mainly to ensure that any included UNUSED_SOURCES that need to be generated get generated)
+ //
+ // Unfortunately, Symbian build chain doesn't support linking generated objects to target,
+ // so supporting generating sources is the best we can do. This is enough for mocs.
+
+ if (targetType != TypeSubdirs) {
+ writeExtraTargets(t);
+ writeExtraCompilerTargets(t);
+
+ t << CREATE_TEMPS_TARGET ":" << endl;
+ // generate command lines like this ...
+ // -@ if NOT EXIST ".\somedir" mkdir ".\somedir"
+ QStringList dirsToClean;
+ QString dirExists = var("QMAKE_CHK_DIR_EXISTS");
+ QString mkdir = var("QMAKE_MKDIR");
+ for (QMap<QString, QStringList>::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) {
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ if (values.at(i).endsWith("/" QT_EXTRA_INCLUDE_DIR)) {
+ QString fixedValue(QDir::toNativeSeparators(values.at(i)));
+ dirsToClean << fixedValue;
+ t << "\t-@ " << dirExists << " \"" << fixedValue << "\" "
+ << (isWindowsShell() ? "" : "|| ")
+ << mkdir << " \"" << fixedValue << "\"" << endl;
+ }
+ }
+ }
+ t << endl;
+
+ // Note: EXTENSION_CLEAN will get called many times when doing reallyclean
+ // This is why the "2> NUL" gets appended to generated clean targets in makefile.cpp.
+ t << EXTENSION_CLEAN ": " COMPILER_CLEAN_TARGET << endl;
+ generateCleanCommands(t, dirsToClean, "$(DEL_TREE)", "", "", "");
+ generateCleanCommands(t, project->values("QMAKE_CLEAN"), "$(DEL_FILE)", "", "", "");
+ t << endl;
+
+ t << PRE_TARGETDEPS_TARGET ":"
+ << MAKEFILE_DEPENDENCY_SEPARATOR GENERATED_SOURCES_TARGET
+ << MAKEFILE_DEPENDENCY_SEPARATOR ALL_SOURCE_DEPS_TARGET;
+ if (project->values("PRE_TARGETDEPS").size())
+ t << MAKEFILE_DEPENDENCY_SEPARATOR << project->values("PRE_TARGETDEPS").join(MAKEFILE_DEPENDENCY_SEPARATOR);
+ t << endl << endl;
+ t << GENERATED_SOURCES_TARGET ":";
+ if (project->values("GENERATED_SOURCES").size())
+ t << MAKEFILE_DEPENDENCY_SEPARATOR << project->values("GENERATED_SOURCES").join(MAKEFILE_DEPENDENCY_SEPARATOR);
+ t << endl << endl;
+ t << ALL_SOURCE_DEPS_TARGET ":";
+
+ QStringList allDeps;
+ for (QMap<QString, QStringList>::iterator it = sources.begin(); it != sources.end(); ++it) {
+ QString currentSourcePath = it.key();
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ // we need additional check
+ QString sourceFile = currentSourcePath + "/" + values.at(i);
+ QStringList deps = findDependencies(QDir::toNativeSeparators(sourceFile));
+ appendIfnotExist(allDeps, deps);
+ }
+ }
+
+ foreach(QString item, allDeps) {
+ t << MAKEFILE_DEPENDENCY_SEPARATOR << item;
+ }
+ t << endl << endl;
+
+ // Post link operations
+ t << FINALIZE_TARGET ":" << endl;
+ if (!project->isEmpty("QMAKE_POST_LINK")) {
+ t << '\t' << var("QMAKE_POST_LINK");
+ t << endl;
+ }
+ t << endl;
+ } else {
+ QList<MakefileGenerator::SubTarget*> subtargets = findSubDirsSubTargets();
+ writeSubTargets(t, subtargets, SubTargetSkipDefaultVariables | SubTargetSkipDefaultTargets);
+ qDeleteAll(subtargets);
+ }
+
+ // Deploymend targets for both emulator and rom deployment
+ writeDeploymentTargets(t, false);
+ writeDeploymentTargets(t, true);
+
+ generateDistcleanTargets(t);
+
+ t << "clean: $(ABLD)" << endl;
+ t << "\t-$(ABLD)" << testClause << " reallyclean" << endl;
+ t << "\t-bldmake clean" << endl;
+ t << endl;
+
+ t << "clean-debug: $(ABLD)" << endl;
+ foreach(QString item, debugPlatforms) {
+ t << "\t$(ABLD)" << testClause << " reallyclean " << item << " udeb" << endl;
+ }
+ t << endl;
+ t << "clean-release: $(ABLD)" << endl;
+ foreach(QString item, releasePlatforms) {
+ t << "\t$(ABLD)" << testClause << " reallyclean " << item << " urel" << endl;
+ }
+ t << endl;
+
+ // For more specific builds, targets are in this form: clean-build-platform, e.g. clean-release-armv5
+ foreach(QString item, debugPlatforms) {
+ t << "clean-debug-" << item << ": $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " reallyclean " << item << " udeb" << endl;
+ }
+ foreach(QString item, releasePlatforms) {
+ t << "clean-release-" << item << ": $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " reallyclean " << item << " urel" << endl;
+ }
+ t << endl;
+
+ t << "freeze: $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " freeze" << endl;
+ t << endl;
+
+ // Abld toolchain doesn't differentiate between freezing release or debug
+ t << "freeze-debug: freeze" << endl << endl;
+ t << "freeze-release: freeze" << endl << endl;
+
+ // For more specific builds, targets are in this form: freeze-build-platform, e.g. freeze-release-armv5,
+ // though note that debug and release targets of each platform are identical in symbian-abld.
+ foreach(QString item, debugPlatforms) {
+ t << "freeze-debug-" << item << ": $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " freeze " << item << endl;
+ }
+ foreach(QString item, releasePlatforms) {
+ t << "freeze-release-" << item << ": $(ABLD)" << endl;
+ t << "\t$(ABLD)" << testClause << " freeze " << item << endl;
+ }
+
+ t << endl;
+}
+
+void SymbianAbldMakefileGenerator::writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile)
+{
+ // We don't use extensions for anything in abld
+ Q_UNUSED(t);
+ Q_UNUSED(iconTargetFile);
+}
+
+bool SymbianAbldMakefileGenerator::writeDeploymentTargets(QTextStream &t, bool isRom)
+{
+ if (isRom)
+ t << DEPLOYMENT_TARGET ":" << endl;
+ else
+ t << WINSCW_DEPLOYMENT_TARGET ":" << endl;
+
+ QString remoteTestPath = qt_epocRoot()
+ + QDir::toNativeSeparators(QLatin1String(isRom ? "epoc32/data/z/private/"
+ : "epoc32/winscw/c/private/"))
+ + privateDirUid;
+ DeploymentList depList;
+
+ initProjectDeploySymbian(project, depList, remoteTestPath, false, true,
+ QLatin1String(isRom ? ROM_DEPLOYMENT_PLATFORM : EMULATOR_DEPLOYMENT_PLATFORM),
+ QString(), generatedDirs, generatedFiles);
+
+ if (depList.size())
+ t << "\t-echo Deploying changed files..." << endl;
+
+ for (int i = 0; i < depList.size(); ++i) {
+#ifdef Q_OS_WIN32
+ // Xcopy prompts for selecting file or directory if target doesn't exist,
+ // and doesn't provide switch to force file selection. It does provide dir forcing, though,
+ // so strip the last part of the destination.
+ t << "\t-$(XCOPY) \"" << depList.at(i).from << "\" \""
+ << depList.at(i).to.left(depList.at(i).to.lastIndexOf("\\") + 1) << "\"" << endl;
+#else
+ QString dirExists = var("QMAKE_CHK_DIR_EXISTS");
+ QString mkdir = var("QMAKE_MKDIR");
+ QString dir = QFileInfo(depList.at(i).to).dir().path();
+ t << "\t-@ " << dirExists << " \"" << dir << "\" || "
+ << mkdir << " \"" << dir << "\"" << endl;
+ t << "\t-$(XCOPY) \"" << QDir::toNativeSeparators(depList.at(i).from) << "\" \""
+ << QDir::toNativeSeparators(depList.at(i).to) << "\"" << endl;
+#endif
+ }
+
+ t << endl;
+
+ if (isRom)
+ t << DEPLOYMENT_CLEAN_TARGET ":" << endl;
+ else
+ t << WINSCW_DEPLOYMENT_CLEAN_TARGET ":" << endl;
+
+ QStringList cleanList;
+ for (int i = 0; i < depList.size(); ++i) {
+ cleanList.append(QDir::toNativeSeparators(depList.at(i).to));
+ }
+ generateCleanCommands(t, cleanList, "$(DEL_FILE)", "", "", "");
+
+ // Note: If deployment creates any directories, they will not get deleted after cleanup.
+ // To do this in robust fashion could be quite complex.
+
+ t << endl;
+
+ return true;
+}
+
+void SymbianAbldMakefileGenerator::writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension)
+{
+ // Normally emulator deployment gets done via regular makefile, but since subdirs
+ // do not get that, special deployment only makefile is generated for them if needed.
+ if (targetType != TypeSubdirs || addDeploymentExtension) {
+ gnuMakefileName = QLatin1String("Makefile_") + fileInfo(mmpFileName).completeBaseName()
+ + QLatin1String(".mk");
+ t << "gnumakefile " << gnuMakefileName << endl;
+ }
+}
+
+void SymbianAbldMakefileGenerator::appendAbldTempDirs(QStringList& sysincspaths, QString includepath)
+{
+ // As a workaround for Symbian toolchain insistence to treat include
+ // statements as relative to source file rather than the file they appear in,
+ // we generate extra temporary include directories to make
+ // relative include paths used in various headers to work properly.
+ // Note that this is not a fix-all solution; it's just a stop-gap measure
+ // to make Qt itself build until toolchain can support relative includes in
+ // a way that Qt expects.
+ QString epocPath("epoc32");
+ if (!includepath.contains(epocPath)) // No temp dirs for epoc includes
+ appendIfnotExist(sysincspaths, includepath + QString("/" QT_EXTRA_INCLUDE_DIR));
+}
diff --git a/qmake/generators/symbian/symmake_abld.h b/qmake/generators/symbian/symmake_abld.h
new file mode 100644
index 0000000000..3ac27b0ccc
--- /dev/null
+++ b/qmake/generators/symbian/symmake_abld.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMMAKE_ABLD_H
+#define SYMMAKE_ABLD_H
+
+#include <symmake.h>
+
+QT_BEGIN_NAMESPACE
+
+class SymbianAbldMakefileGenerator : public SymbianMakefileGenerator
+{
+protected:
+
+ // Inherited from parent
+ virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile);
+ virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension);
+ virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly);
+ virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile);
+ virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath);
+
+ bool writeDeploymentTargets(QTextStream &t, bool isRom);
+ QString gnuMakefileName;
+public:
+
+ SymbianAbldMakefileGenerator();
+ ~SymbianAbldMakefileGenerator();
+};
+
+#endif // SYMMAKE_ABLD_H
diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp
new file mode 100644
index 0000000000..767645ac2d
--- /dev/null
+++ b/qmake/generators/symbian/symmake_sbsv2.cpp
@@ -0,0 +1,760 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symmake_sbsv2.h"
+#include "initprojectdeploy_symbian.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+
+// Included from tools/shared
+#include <symbian/epocroot_p.h>
+
+SymbianSbsv2MakefileGenerator::SymbianSbsv2MakefileGenerator() : SymbianMakefileGenerator() { }
+SymbianSbsv2MakefileGenerator::~SymbianSbsv2MakefileGenerator() { }
+
+#define FLM_DEST_DIR "epoc32/tools/makefile_templates/qt"
+#define FLM_SOURCE_DIR "/mkspecs/symbian-sbsv2/flm/qt"
+#define PLATFORM_GCCE "gcce"
+#define PLATFORM_WINSCW "winscw"
+#define PLATFORM_ARM_PREFIX "arm"
+#define BUILD_DEBUG "udeb"
+#define BUILD_RELEASE "urel"
+#define SBS_RVCT_PREFIX "rvct"
+
+static QString winscwPlatform;
+static QString armPlatformPrefix;
+static QString gccePlatform;
+static QString sbsRvctPrefix;
+
+#if defined(Q_OS_UNIX)
+ extern char **environ;
+#endif
+
+static void fixFlmCmd(QString *cmdLine, const QMap<QString, QString> &commandsToReplace)
+{
+ // If commandItem starts with any $$QMAKE_* commands, do a replace for SBS equivalent.
+ // Command replacement is done only for the start of the command or right after
+ // concatenation operators (&& and ||), as otherwise unwanted replacements might occur.
+ static QString cmdFind(QLatin1String("(^|&&\\s*|\\|\\|\\s*)%1"));
+ static QString cmdReplace(QLatin1String("\\1%1"));
+
+ // $$escape_expand(\\n\\t) doesn't work for bld.inf files, but is often used as command
+ // separator, so replace it with "&&" command concatenator.
+ cmdLine->replace("\n\t", "&&");
+
+ // Strip output suppression, as sbsv2 can't handle it in FLMs. Cannot be done by simply
+ // adding "@" to commandsToReplace, as it'd get handled last due to alphabetical ordering,
+ // potentially masking other commands that need replacing.
+ if (cmdLine->contains("@"))
+ cmdLine->replace(QRegExp(cmdFind.arg("@")), cmdReplace.arg(""));
+
+ // Iterate command replacements in reverse alphabetical order of keys so
+ // that keys which are starts of other longer keys are iterated after longer keys.
+ QMapIterator<QString, QString> cmdIter(commandsToReplace);
+ cmdIter.toBack();
+ while (cmdIter.hasPrevious()) {
+ cmdIter.previous();
+ if (cmdLine->contains(cmdIter.key()))
+ cmdLine->replace(QRegExp(cmdFind.arg(cmdIter.key())), cmdReplace.arg(cmdIter.value()));
+ }
+
+ // Sbsv2 toolchain strips all backslashes (even double ones) from option parameters, so just
+ // assume all backslashes are directory separators and replace them with slashes.
+ // Problem: If some command actually needs backslashes for something else than dir separator,
+ // we are out of luck.
+ cmdLine->replace("\\", "/");
+}
+
+// Copies Qt FLMs to correct location under epocroot.
+// This is not done by configure as it is possible to change epocroot after configure.
+void SymbianSbsv2MakefileGenerator::exportFlm()
+{
+ static bool flmExportDone = false;
+
+ if (!flmExportDone) {
+ QDir sourceDir = QDir(QLibraryInfo::location(QLibraryInfo::PrefixPath) + FLM_SOURCE_DIR);
+ QFileInfoList sourceInfos = sourceDir.entryInfoList(QDir::Files);
+
+ QDir destDir(qt_epocRoot() + FLM_DEST_DIR);
+ if (!destDir.exists()) {
+ if (destDir.mkpath(destDir.absolutePath()))
+ generatedDirs << destDir.absolutePath();
+ }
+
+ foreach(QFileInfo item, sourceInfos) {
+ QFileInfo destInfo = QFileInfo(destDir.absolutePath() + "/" + item.fileName());
+ if (!destInfo.exists() || destInfo.lastModified() != item.lastModified()) {
+ if (destInfo.exists())
+ QFile::remove(destInfo.absoluteFilePath());
+ if (QFile::copy(item.absoluteFilePath(), destInfo.absoluteFilePath()))
+ generatedFiles << destInfo.absoluteFilePath();
+ else
+ fprintf(stderr, "Error: Could not copy '%s' -> '%s'\n",
+ qPrintable(item.absoluteFilePath()),
+ qPrintable(destInfo.absoluteFilePath()));
+ }
+ }
+ flmExportDone = true;
+ }
+}
+
+void SymbianSbsv2MakefileGenerator::findInstalledCompilerVersions(const QString &matchExpression,
+ const QString &versionPrefix,
+ QStringList *versionList)
+{
+ // No need to be able to find env variables on other operating systems,
+ // as only linux and windows have support for symbian-sbsv2 toolchain
+#if defined(Q_OS_UNIX) || defined(Q_OS_WIN)
+ char *entry = 0;
+ int count = 0;
+ QRegExp matcher(matchExpression);
+ while ((entry = environ[count++])) {
+ if (matcher.exactMatch(QString::fromLocal8Bit(entry))
+ && fileInfo(matcher.cap(matcher.captureCount())).exists()) {
+ // First capture (index 0) is the whole match, which is skipped.
+ // Next n captures are version numbers, which are interesting.
+ // Final capture is the env var value, which we already used, so that is skipped, too.
+ int capture = 1;
+ int finalCapture = matcher.captureCount() - 1;
+ QString version = versionPrefix;
+ while (capture <= finalCapture) {
+ version.append(matcher.cap(capture));
+ if (capture != finalCapture)
+ version.append(QLatin1Char('.'));
+ capture++;
+ }
+ *versionList << version;
+ }
+ }
+#endif
+}
+
+void SymbianSbsv2MakefileGenerator::findGcceVersions(QStringList *gcceVersionList,
+ QString *defaultVersion)
+{
+ QString matchStr = QLatin1String("SBS_GCCE(\\d)(\\d)(\\d)BIN=(.*)");
+ findInstalledCompilerVersions(matchStr, gccePlatform, gcceVersionList);
+
+ QString qtGcceVersion = QString::fromLocal8Bit(qgetenv("QT_GCCE_VERSION"));
+
+ if (!qtGcceVersion.isEmpty()) {
+ if (QRegExp("\\d+\\.\\d+\\.\\d+").exactMatch(qtGcceVersion)) {
+ *defaultVersion = gccePlatform + qtGcceVersion;
+ } else {
+ fprintf(stderr, "Warning: Variable QT_GCCE_VERSION ('%s') is in incorrect "
+ "format, expected format is: 'x.y.z'. Attempting to autodetect GCCE version.\n",
+ qPrintable(qtGcceVersion));
+ }
+ }
+
+ if (defaultVersion->isEmpty() && gcceVersionList->size()) {
+ gcceVersionList->sort();
+ *defaultVersion = gcceVersionList->last();
+ }
+}
+
+void SymbianSbsv2MakefileGenerator::findRvctVersions(QStringList *rvctVersionList,
+ QString *defaultVersion)
+{
+ QString matchStr = QLatin1String("RVCT(\\d)(\\d)BIN=(.*)");
+ findInstalledCompilerVersions(matchStr, sbsRvctPrefix, rvctVersionList);
+
+ QString qtRvctVersion = QString::fromLocal8Bit(qgetenv("QT_RVCT_VERSION"));
+
+ if (!qtRvctVersion.isEmpty()) {
+ if (QRegExp("\\d+\\.\\d+").exactMatch(qtRvctVersion)) {
+ *defaultVersion = sbsRvctPrefix + qtRvctVersion;
+ } else {
+ fprintf(stderr, "Warning: Variable QT_RVCT_VERSION ('%s') is in incorrect "
+ "format, expected format is: 'x.y'.\n",
+ qPrintable(qtRvctVersion));
+ }
+ }
+}
+
+QString SymbianSbsv2MakefileGenerator::configClause(const QString &platform,
+ const QString &build,
+ const QString &compilerVersion,
+ const QString &clauseTemplate)
+{
+ QString retval;
+ if (QString::compare(platform, winscwPlatform) == 0) {
+ retval = clauseTemplate.arg(build);
+ } else if (platform.startsWith(armPlatformPrefix)) {
+ QString fixedCompilerVersion = compilerVersion;
+ fixedCompilerVersion.replace(".","_");
+ retval = clauseTemplate.arg(platform.mid(sizeof(PLATFORM_ARM_PREFIX)-1))
+ .arg(build)
+ .arg(fixedCompilerVersion);
+ } // else - Unsupported platform for makefile target, return empty clause
+ return retval;
+}
+
+void SymbianSbsv2MakefileGenerator::writeSbsDeploymentList(const DeploymentList& depList, QTextStream& t)
+{
+ for (int i = 0; i < depList.size(); ++i) {
+ t << "START EXTENSION qt/qmake_emulator_deployment" << endl;
+ QString fromItem = depList.at(i).from;
+ QString toItem = depList.at(i).to;
+ fromItem.replace("\\", "/");
+ toItem.replace("\\", "/");
+#if defined(Q_OS_WIN)
+ // add drive if it doesn't have one yet
+ if (toItem.size() > 1 && toItem[1] != QLatin1Char(':'))
+ toItem.prepend(QDir::current().absolutePath().left(2));
+#endif
+ t << "OPTION DEPLOY_SOURCE " << fromItem << endl;
+ t << "OPTION DEPLOY_TARGET " << toItem << endl;
+ t << "END" << endl;
+ }
+}
+
+void SymbianSbsv2MakefileGenerator::writeMkFile(const QString& wrapperFileName, bool deploymentOnly)
+{
+ // Can't use extension makefile with sbsv2
+ Q_UNUSED(wrapperFileName);
+ Q_UNUSED(deploymentOnly);
+}
+
+void SymbianSbsv2MakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile)
+{
+ static QString debugBuild;
+ static QString releaseBuild;
+ static QString defaultGcceCompilerVersion;
+ static QString defaultRvctCompilerVersion;
+ static QStringList rvctVersions;
+ static QStringList gcceVersions;
+ static QStringList allArmCompilerVersions;
+
+ // Initialize static variables used in makefile creation
+ if (debugBuild.isEmpty()) {
+ debugBuild.append(QLatin1String(BUILD_DEBUG));
+ releaseBuild.append(QLatin1String(BUILD_RELEASE));
+ winscwPlatform.append(QLatin1String(PLATFORM_WINSCW));
+ gccePlatform.append(QLatin1String(PLATFORM_GCCE));
+ armPlatformPrefix.append(QLatin1String(PLATFORM_ARM_PREFIX));
+ sbsRvctPrefix.append(QLatin1String(SBS_RVCT_PREFIX));
+
+ findGcceVersions(&gcceVersions, &defaultGcceCompilerVersion);
+ findRvctVersions(&rvctVersions, &defaultRvctCompilerVersion);
+
+ allArmCompilerVersions << rvctVersions << gcceVersions;
+
+ if (!allArmCompilerVersions.size()) {
+ fprintf(stderr, "Warning: No HW compilers detected. "
+ "Please install either GCCE or RVCT compiler to enable release builds.\n");
+ }
+ }
+
+ QStringList allPlatforms;
+ foreach(QString platform, project->values("SYMBIAN_PLATFORMS")) {
+ allPlatforms << platform.toLower();
+ }
+
+ if (!gcceVersions.size())
+ allPlatforms.removeAll(gccePlatform);
+
+ QString testClause;
+ if (project->isActiveConfig(SYMBIAN_TEST_CONFIG))
+ testClause = QLatin1String(".test");
+ else
+ testClause = QLatin1String("");
+
+ // Note: armClause is used for gcce, too, which has a side effect
+ // of requiring armv* platform(s) in SYMBIAN_PLATFORMS in order
+ // to get any compiler version specific targets.
+ QString armClause = " -c " PLATFORM_ARM_PREFIX ".%1.%2.%3" + testClause;
+ QString genericArmClause;
+ if (defaultRvctCompilerVersion.isEmpty()) {
+ // Note: Argument %3 needs to be empty string in this version of clause
+ genericArmClause = " -c " PLATFORM_ARM_PREFIX "%1_%2%3" + testClause;
+ } else {
+ // If defaultRvctCompilerVersion is defined, use specific sbs clause for "generic" clause
+ genericArmClause = armClause;
+ }
+ QString winscwClause = " -c " PLATFORM_WINSCW "_%1.mwccinc" + testClause;;
+
+ QStringList armPlatforms = allPlatforms.filter(QRegExp("^" PLATFORM_ARM_PREFIX));
+
+ if (!allArmCompilerVersions.size()) {
+ foreach (QString item, armPlatforms) {
+ allPlatforms.removeAll(item);
+ }
+ armPlatforms.clear();
+ }
+
+ QStringList allClauses;
+ QStringList debugClauses;
+ QStringList releaseClauses;
+
+ // Only winscw and arm platforms are supported
+ QStringList debugPlatforms = allPlatforms;
+ QStringList releasePlatforms = allPlatforms;
+ releasePlatforms.removeAll(winscwPlatform); // No release for emulator
+
+ if (!releasePlatforms.size()) {
+ fprintf(stderr, "Warning: No valid release platforms in SYMBIAN_PLATFORMS (%s)\n"
+ "Most likely required compiler(s) are not properly installed.\n",
+ qPrintable(project->values("SYMBIAN_PLATFORMS").join(" ")));
+ }
+
+ if (debugPlatforms.contains(winscwPlatform))
+ debugClauses << configClause(winscwPlatform, debugBuild, QString(), winscwClause);
+
+ foreach(QString item, armPlatforms) {
+ // Only use single clause per arm platform even if multiple compiler versions were found,
+ // otherwise we get makefile target collisions from sbsv2 toolchain.
+ if (rvctVersions.size()) {
+ debugClauses << configClause(item, debugBuild, defaultRvctCompilerVersion, genericArmClause);
+ releaseClauses << configClause(item, releaseBuild, defaultRvctCompilerVersion, genericArmClause);
+ } else {
+ debugClauses << configClause(item, debugBuild, defaultGcceCompilerVersion, armClause);
+ releaseClauses << configClause(item, releaseBuild, defaultGcceCompilerVersion, armClause);
+ }
+ }
+
+ allClauses << debugClauses << releaseClauses;
+
+ QTextStream t(&wrapperFile);
+
+ MakefileGenerator::writeHeader(t);
+
+ t << "MAKEFILE = " << fileInfo(wrapperFile.fileName()).fileName() << endl;
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
+ t << "CHK_DIR_EXISTS = " << var("QMAKE_CHK_DIR_EXISTS") << endl;
+ t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
+ t << "MOVE = " << var("QMAKE_MOVE") << endl;
+ t << "DEBUG_PLATFORMS = " << debugPlatforms.join(" ") << endl;
+ t << "RELEASE_PLATFORMS = " << releasePlatforms.join(" ") << endl;
+ t << "MAKE = make" << endl;
+ t << "SBS = sbs" << endl;
+ t << endl;
+ t << "DEFINES" << '\t' << " = "
+ << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ")
+ << varGlue("DEFINES","-D"," -D","") << endl;
+
+ t << "INCPATH" << '\t' << " = ";
+
+ for (QMap<QString, QStringList>::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) {
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ t << " -I\"" << values.at(i) << "\" ";
+ }
+ }
+
+ t << endl;
+ t << "first: default" << endl << endl;
+ if (!isPrimaryMakefile) {
+ t << "all:" << endl << endl;
+ t << "default: all" << endl << endl;
+ } else {
+ t << "all: debug release" << endl << endl;
+ if (debugPlatforms.contains(winscwPlatform))
+ t << "default: debug-winscw";
+ else if (debugPlatforms.size())
+ t << "default: debug-" << debugPlatforms.first();
+ else
+ t << "default: all";
+ t << endl;
+
+ QString qmakeCmd = "\t$(QMAKE) \"" + project->projectFile() + "\" " + buildArgs();
+
+ t << "qmake:" << endl;
+ t << qmakeCmd << endl;
+ t << endl;
+
+ t << BLD_INF_FILENAME ": " << project->projectFile() << endl;
+ t << qmakeCmd << endl;
+ t << endl;
+
+ QString locFileDep = generateLocFileTarget(t, qmakeCmd);
+
+ t << "debug: " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)";
+ foreach(QString clause, debugClauses) {
+ t << clause;
+ }
+ t << endl;
+ t << "clean-debug: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean --toolcheck=off";
+ foreach(QString clause, debugClauses) {
+ t << clause;
+ }
+ t << endl;
+
+ t << "freeze-debug: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze";
+ foreach(QString clause, debugClauses) {
+ t << clause;
+ }
+ t << endl;
+
+ t << "release: " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)";
+ foreach(QString clause, releaseClauses) {
+ t << clause;
+ }
+ t << endl;
+ t << "clean-release: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean --toolcheck=off";
+ foreach(QString clause, releaseClauses) {
+ t << clause;
+ }
+ t << endl;
+
+ t << "freeze-release: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze";
+ foreach(QString clause, releaseClauses) {
+ t << clause;
+ }
+ t << endl << endl;
+
+ QString defaultGcceArmVersion;
+ if (armPlatforms.size()) {
+ defaultGcceArmVersion = armPlatforms.first();
+ } else {
+ defaultGcceArmVersion = QLatin1String("armv5");
+ }
+
+ // For more specific builds, targets are in this form:
+ // release-armv5 - generic target, compiler version determined by toolchain or autodetection
+ // release-armv5-rvct4.0 - compiler version specific target
+ foreach(QString item, debugPlatforms) {
+ QString clause;
+ if (item.compare(winscwPlatform) == 0)
+ clause = configClause(item, debugBuild, QString(), winscwClause);
+ else if (item.compare(gccePlatform) == 0 )
+ clause = configClause(defaultGcceArmVersion, debugBuild, defaultGcceCompilerVersion, armClause);
+ else // use generic arm clause
+ clause = configClause(item, debugBuild, defaultRvctCompilerVersion, genericArmClause);
+
+ t << "debug-" << item << ": " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)" << clause << endl;
+ t << "clean-debug-" << item << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean" << clause << endl;
+ t << "freeze-debug-" << item << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze" << clause << endl;
+ }
+
+ foreach(QString item, releasePlatforms) {
+ QString clause;
+ if (item.compare(gccePlatform) == 0 )
+ clause = configClause(defaultGcceArmVersion, releaseBuild, defaultGcceCompilerVersion, armClause);
+ else // use generic arm clause
+ clause = configClause(item, releaseBuild, defaultRvctCompilerVersion, genericArmClause);
+
+ t << "release-" << item << ": " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)" << clause << endl;
+ t << "clean-release-" << item << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean" << clause << endl;
+ t << "freeze-release-" << item << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze" << clause << endl;
+ }
+
+ foreach(QString item, armPlatforms) {
+ foreach(QString compilerVersion, allArmCompilerVersions) {
+ QString debugClause = configClause(item, debugBuild, compilerVersion, armClause);
+ QString releaseClause = configClause(item, releaseBuild, compilerVersion, armClause);
+ t << "debug-" << item << "-" << compilerVersion << ": " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)" << debugClause << endl;
+ t << "clean-debug-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean" << debugClause << endl;
+ t << "freeze-debug-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze" << debugClause << endl;
+ t << "release-" << item << "-" << compilerVersion << ": " << locFileDep << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS)" << releaseClause << endl;
+ t << "clean-release-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) reallyclean" << releaseClause << endl;
+ t << "freeze-release-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) freeze" << releaseClause << endl;
+ }
+ }
+
+ t << endl;
+ t << "export: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) export";
+ foreach(QString clause, allClauses) {
+ t << clause;
+ }
+ t << endl << endl;
+
+ t << "cleanexport: " << BLD_INF_FILENAME << endl;
+ t << "\t$(SBS) cleanexport";
+ foreach(QString clause, allClauses) {
+ t << clause;
+ }
+ t << endl << endl;
+
+ // Typically one wants to freeze release binaries, so make plain freeze target equal to
+ // freeze-release. If freezing of debug binaries is needed for some reason, then
+ // freeze-debug target should be used. There is no point to try freezing both with one
+ // target as both produce the same def file.
+ t << "freeze: freeze-release" << endl << endl;
+ }
+
+ // Add all extra targets including extra compiler targets also to wrapper makefile,
+ // even though many of them may have already been added to bld.inf as FLMs.
+ // This is to enable use of targets like 'mocables', which call targets generated by extra compilers.
+ if (targetType != TypeSubdirs) {
+ t << extraTargetsCache;
+ t << extraCompilersCache;
+ } else {
+ QList<MakefileGenerator::SubTarget*> subtargets = findSubDirsSubTargets();
+ writeSubTargets(t, subtargets, SubTargetSkipDefaultVariables|SubTargetSkipDefaultTargets);
+ qDeleteAll(subtargets);
+ }
+
+ generateDistcleanTargets(t);
+
+ // Do not check for tools when doing generic clean, as most tools are not actually needed for
+ // cleaning. Mainly this is relevant for environments that do not have winscw compiler.
+ t << "clean: " << BLD_INF_FILENAME << endl;
+ t << "\t-$(SBS) reallyclean --toolcheck=off";
+ foreach(QString clause, allClauses) {
+ t << clause;
+ }
+ t << endl << endl;
+
+ t << endl;
+}
+
+void SymbianSbsv2MakefileGenerator::writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile)
+{
+ // Makes sure we have needed FLMs in place.
+ exportFlm();
+
+ // Parse extra compilers data
+ QStringList defines;
+ QStringList incPath;
+
+ defines << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ")
+ << varGlue("DEFINES","-D"," -D","");
+ for (QMap<QString, QStringList>::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) {
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ incPath << QLatin1String(" -I\"") + values.at(i) + "\"";
+ }
+ }
+
+ QMap<QString, QString> commandsToReplace;
+ commandsToReplace.insert(project->values("QMAKE_COPY").join(" "),
+ project->values("QMAKE_SBSV2_COPY").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_COPY_DIR").join(" "),
+ project->values("QMAKE_SBSV2_COPY_DIR").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_MOVE").join(" "),
+ project->values("QMAKE_SBSV2_MOVE").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_DEL_FILE").join(" "),
+ project->values("QMAKE_SBSV2_DEL_FILE").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_MKDIR").join(" "),
+ project->values("QMAKE_SBSV2_MKDIR").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_DEL_DIR").join(" "),
+ project->values("QMAKE_SBSV2_DEL_DIR").join(" "));
+ commandsToReplace.insert(project->values("QMAKE_DEL_TREE").join(" "),
+ project->values("QMAKE_SBSV2_DEL_TREE").join(" "));
+
+ // Write extra compilers and targets to initialize QMAKE_ET_* variables
+ // Cache results to avoid duplicate calls when creating wrapper makefile
+ QTextStream extraCompilerStream(&extraCompilersCache);
+ QTextStream extraTargetStream(&extraTargetsCache);
+ writeExtraCompilerTargets(extraCompilerStream);
+ writeExtraTargets(extraTargetStream);
+
+ // Figure out everything the target depends on as we don't want to run extra targets that
+ // are not necessary.
+ QStringList allPreDeps;
+ foreach(QString item, project->values("PRE_TARGETDEPS")) {
+ allPreDeps.append(fileInfo(item).absoluteFilePath());
+ }
+
+ foreach (QString item, project->values("GENERATED_SOURCES")) {
+ allPreDeps.append(fileInfo(item).absoluteFilePath());
+ }
+
+ for (QMap<QString, QStringList>::iterator it = sources.begin(); it != sources.end(); ++it) {
+ QString currentSourcePath = it.key();
+ QStringList values = it.value();
+ for (int i = 0; i < values.size(); ++i) {
+ QString sourceFile = currentSourcePath + "/" + values.at(i);
+ QStringList deps = findDependencies(QDir::toNativeSeparators(sourceFile));
+ foreach(QString depItem, deps) {
+ appendIfnotExist(allPreDeps, fileInfo(depItem).absoluteFilePath());
+ }
+ }
+ }
+
+ // Write FLM rules for all extra targets and compilers that we depend on to build the target.
+ QStringList extraTargets;
+ extraTargets << project->values("QMAKE_EXTRA_TARGETS") << project->values("QMAKE_EXTRA_COMPILERS");
+ foreach(QString item, extraTargets) {
+ foreach(QString targetItem, project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + item)) {
+ // Make sure targetpath is absolute
+ QString absoluteTarget = fileInfo(targetItem).absoluteFilePath();
+ if (allPreDeps.contains(absoluteTarget)) {
+ QStringList deps = project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + item + targetItem);
+ QString commandItem = project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + item + targetItem).join(" ");
+
+ // Make sure all deps paths are absolute
+ QString absoluteDeps;
+ foreach (QString depItem, deps) {
+ if (!depItem.isEmpty()) {
+ absoluteDeps.append(fileInfo(depItem).absoluteFilePath());
+ absoluteDeps.append(" ");
+ }
+ }
+
+ t << "START EXTENSION qt/qmake_extra_pre_targetdep.export" << endl;
+ t << "OPTION PREDEP_TARGET " << absoluteTarget << endl;
+ t << "OPTION DEPS " << absoluteDeps << endl;
+
+ if (commandItem.indexOf("$(INCPATH)") != -1)
+ commandItem.replace("$(INCPATH)", incPath.join(" "));
+ if (commandItem.indexOf("$(DEFINES)") != -1)
+ commandItem.replace("$(DEFINES)", defines.join(" "));
+
+ fixFlmCmd(&commandItem, commandsToReplace);
+
+ t << "OPTION COMMAND " << commandItem << endl;
+ t << "END" << endl;
+ }
+ }
+ }
+
+ t << endl;
+
+ // Write deployment rules
+ QString remoteTestPath = qt_epocRoot() + QLatin1String("epoc32/winscw/c/private/") + privateDirUid;
+ DeploymentList depList;
+
+ //write emulator deployment
+ t << "#if defined(WINSCW)" << endl;
+ initProjectDeploySymbian(project, depList, remoteTestPath, false, true,
+ QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM), QString(), generatedDirs, generatedFiles);
+ writeSbsDeploymentList(depList, t);
+ t << "#endif" << endl;
+
+ //write ROM deployment
+ remoteTestPath = qt_epocRoot() + QLatin1String("epoc32/data/z/private/") + privateDirUid;
+ depList.clear();
+ initProjectDeploySymbian(project, depList, remoteTestPath, false, true,
+ QLatin1String(ROM_DEPLOYMENT_PLATFORM), QString(), generatedDirs, generatedFiles);
+ writeSbsDeploymentList(depList, t);
+ t << endl;
+
+ // Write post link rules
+ if (!project->isEmpty("QMAKE_POST_LINK")) {
+ QString postLinkCmd = var("QMAKE_POST_LINK");
+ fixFlmCmd(&postLinkCmd, commandsToReplace);
+ t << "START EXTENSION qt/qmake_post_link" << endl;
+ t << "OPTION POST_LINK_CMD " << postLinkCmd << endl;
+ t << "OPTION LINK_TARGET " << fixedTarget << QLatin1String(".") << getTargetExtension() << endl;
+ t << "END" << endl;
+ t << endl;
+ }
+
+ // Application icon generation
+ QStringList icons = project->values("ICON");
+ if (icons.size()) {
+ QString icon = icons.first();
+ if (icons.size() > 1)
+ fprintf(stderr, "Warning: Only first icon specified in ICON variable is used: '%s'.", qPrintable(icon));
+
+ t << "START EXTENSION s60/mifconv" << endl;
+
+ QFileInfo iconInfo = fileInfo(icon);
+
+ QFileInfo bldinf(project->values("MAKEFILE").first());
+ QString iconPath = bldinf.dir().relativeFilePath(iconInfo.path());
+
+ QString iconFile = iconInfo.baseName();
+
+ QFileInfo iconTargetInfo = fileInfo(iconTargetFile);
+ QString iconTarget = iconTargetInfo.fileName();
+
+ t << "OPTION SOURCES -c32 " << iconFile << endl;
+ t << "OPTION SOURCEDIR " << iconPath << endl;
+ t << "OPTION TARGETFILE " << iconTarget << endl;
+ t << "OPTION SVGENCODINGVERSION 3" << endl; // Compatibility with S60 3.1 devices and up
+ t << "END" << endl;
+ }
+
+ t << "START EXTENSION qt/qmake_store_build" << endl;
+ t << "END" << endl;
+ t << endl;
+
+ // Handle QMAKE_CLEAN
+ QStringList cleanFiles = project->values("QMAKE_CLEAN");
+ if (!cleanFiles.isEmpty()) {
+ QStringList absoluteCleanFiles;
+ foreach (QString cleanFile, cleanFiles) {
+ QFileInfo fi(cleanFile);
+ QString fileName = QLatin1String("\"");
+ fileName.append(fi.absoluteFilePath());
+ fileName.append(QLatin1String("\""));
+ absoluteCleanFiles << fileName;
+ }
+ t << "START EXTENSION qt/qmake_clean" << endl;
+ t << "OPTION CLEAN_FILES " << absoluteCleanFiles.join(" ") << endl;
+ t << "END" << endl;
+ }
+ t << endl;
+}
+
+void SymbianSbsv2MakefileGenerator::writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension)
+{
+ // We don't generate extension makefile in sbsb2
+ Q_UNUSED(t);
+ Q_UNUSED(addDeploymentExtension);
+}
+
+void SymbianSbsv2MakefileGenerator::appendAbldTempDirs(QStringList& sysincspaths, QString includepath)
+{
+ //Do nothing
+ Q_UNUSED(sysincspaths);
+ Q_UNUSED(includepath);
+}
diff --git a/qmake/generators/symbian/symmake_sbsv2.h b/qmake/generators/symbian/symmake_sbsv2.h
new file mode 100644
index 0000000000..bec1034fbe
--- /dev/null
+++ b/qmake/generators/symbian/symmake_sbsv2.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMMAKE_SBSV2_H
+#define SYMMAKE_SBSV2_H
+
+#include <symmake.h>
+
+QT_BEGIN_NAMESPACE
+
+class SymbianSbsv2MakefileGenerator : public SymbianMakefileGenerator
+{
+protected:
+
+ // Inherited from parent
+ virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile);
+ virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension);
+ virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly);
+ virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile);
+ virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath);
+ virtual bool isForSymbianSbsv2() const { return true; } // FIXME: killme - i'm ugly!
+
+public:
+
+ SymbianSbsv2MakefileGenerator();
+ ~SymbianSbsv2MakefileGenerator();
+
+private:
+ void exportFlm();
+ void findGcceVersions(QStringList *gcceVersionList, QString *defaultVersion);
+ void findRvctVersions(QStringList *rvctVersionList, QString *defaultVersion);
+ void findInstalledCompilerVersions(const QString &matchExpression,
+ const QString &versionPrefix,
+ QStringList *versionList);
+ QString configClause(const QString &platform,
+ const QString &build,
+ const QString &compilerVersion,
+ const QString &clauseTemplate);
+
+ void writeSbsDeploymentList(const DeploymentList& depList, QTextStream& t);
+
+ QString extraTargetsCache;
+ QString extraCompilersCache;
+};
+
+#endif // SYMMAKE_SBSV2_H
diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp
new file mode 100644
index 0000000000..e659e62288
--- /dev/null
+++ b/qmake/generators/unix/unixmake.cpp
@@ -0,0 +1,925 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "unixmake.h"
+#include "option.h"
+#include <qregexp.h>
+#include <qfile.h>
+#include <qhash.h>
+#include <qdir.h>
+#include <time.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+void
+UnixMakefileGenerator::init()
+{
+ if(init_flag)
+ return;
+ init_flag = true;
+
+ if(project->isEmpty("QMAKE_EXTENSION_SHLIB")) {
+ if(project->isEmpty("QMAKE_CYGWIN_SHLIB")) {
+ project->values("QMAKE_EXTENSION_SHLIB").append("so");
+ } else {
+ project->values("QMAKE_EXTENSION_SHLIB").append("dll");
+ }
+ }
+
+ if (project->isEmpty("QMAKE_PREFIX_SHLIB"))
+ // Prevent crash when using the empty variable.
+ project->values("QMAKE_PREFIX_SHLIB").append("");
+
+ if(!project->isEmpty("QMAKE_FAILED_REQUIREMENTS")) /* no point */
+ return;
+
+ QStringList &configs = project->values("CONFIG");
+ if(project->isEmpty("ICON") && !project->isEmpty("RC_FILE"))
+ project->values("ICON") = project->values("RC_FILE");
+ if(project->isEmpty("QMAKE_EXTENSION_PLUGIN"))
+ project->values("QMAKE_EXTENSION_PLUGIN").append(project->first("QMAKE_EXTENSION_SHLIB"));
+ if(project->isEmpty("QMAKE_COPY_FILE"))
+ project->values("QMAKE_COPY_FILE").append("$(COPY)");
+ if(project->isEmpty("QMAKE_STREAM_EDITOR"))
+ project->values("QMAKE_STREAM_EDITOR").append("sed");
+ if(project->isEmpty("QMAKE_COPY_DIR"))
+ project->values("QMAKE_COPY_DIR").append("$(COPY) -R");
+ if(project->isEmpty("QMAKE_INSTALL_FILE"))
+ project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)");
+ if(project->isEmpty("QMAKE_INSTALL_DIR"))
+ project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)");
+ if(project->isEmpty("QMAKE_INSTALL_PROGRAM"))
+ project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)");
+ if(project->isEmpty("QMAKE_LIBTOOL"))
+ project->values("QMAKE_LIBTOOL").append("libtool --silent");
+ if(project->isEmpty("QMAKE_SYMBOLIC_LINK"))
+ project->values("QMAKE_SYMBOLIC_LINK").append("ln -f -s");
+
+ /* this should probably not be here, but I'm using it to wrap the .t files */
+ if(project->first("TEMPLATE") == "app")
+ project->values("QMAKE_APP_FLAG").append("1");
+ else if(project->first("TEMPLATE") == "lib")
+ project->values("QMAKE_LIB_FLAG").append("1");
+ else if(project->first("TEMPLATE") == "subdirs") {
+ MakefileGenerator::init();
+ if(project->isEmpty("MAKEFILE"))
+ project->values("MAKEFILE").append("Makefile");
+ if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1)
+ project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all");
+ return; /* subdirs is done */
+ }
+
+ //If the TARGET looks like a path split it into DESTDIR and the resulting TARGET
+ if(!project->isEmpty("TARGET")) {
+ project->values("TARGET") = escapeFilePaths(project->values("TARGET"));
+ QString targ = unescapeFilePath(project->first("TARGET"));
+ int slsh = qMax(targ.lastIndexOf('/'), targ.lastIndexOf(Option::dir_sep));
+ if(slsh != -1) {
+ if(project->isEmpty("DESTDIR"))
+ project->values("DESTDIR").append("");
+ else if(project->first("DESTDIR").right(1) != Option::dir_sep)
+ project->values("DESTDIR") = QStringList(project->first("DESTDIR") + Option::dir_sep);
+ project->values("DESTDIR") = QStringList(project->first("DESTDIR") + targ.left(slsh+1));
+ project->values("TARGET") = QStringList(targ.mid(slsh+1));
+ }
+ }
+
+ project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
+ project->values("QMAKE_ORIG_DESTDIR") = project->values("DESTDIR");
+ project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS"));
+ project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE"));
+ if((!project->isEmpty("QMAKE_LIB_FLAG") && !project->isActiveConfig("staticlib")) ||
+ (project->isActiveConfig("qt") && project->isActiveConfig("plugin"))) {
+ if(configs.indexOf("dll") == -1) configs.append("dll");
+ } else if(!project->isEmpty("QMAKE_APP_FLAG") || project->isActiveConfig("dll")) {
+ configs.removeAll("staticlib");
+ }
+ if(!project->isEmpty("QMAKE_INCREMENTAL"))
+ project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_INCREMENTAL");
+ else if(!project->isEmpty("QMAKE_LFLAGS_PREBIND") &&
+ !project->values("QMAKE_LIB_FLAG").isEmpty() &&
+ project->isActiveConfig("dll"))
+ project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PREBIND");
+ if(!project->isEmpty("QMAKE_INCDIR"))
+ project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
+ if(!project->isEmpty("QMAKE_LIBDIR")) {
+ const QStringList &libdirs = project->values("QMAKE_LIBDIR");
+ for(int i = 0; i < libdirs.size(); ++i) {
+ if(!project->isEmpty("QMAKE_LFLAGS_RPATH") && project->isActiveConfig("rpath_libdirs"))
+ project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + libdirs[i];
+ if (project->isActiveConfig("rvct_linker")) {
+ project->values("QMAKE_LIBDIR_FLAGS") += "--userlibpath " + escapeFilePath(libdirs[i]);
+ } else if (project->isActiveConfig("armcc_linker")) {
+ project->values("QMAKE_LIBDIR_FLAGS") += "-L--userlibpath=" + escapeFilePath(libdirs[i]);
+ } else {
+ project->values("QMAKE_LIBDIR_FLAGS") += "-L" + escapeFilePath(libdirs[i]);
+ }
+ }
+ }
+ if(project->isActiveConfig("macx") && !project->isEmpty("QMAKE_FRAMEWORKPATH")) {
+ const QStringList &fwdirs = project->values("QMAKE_FRAMEWORKPATH");
+ for(int i = 0; i < fwdirs.size(); ++i) {
+ project->values("QMAKE_FRAMEWORKPATH_FLAGS") += "-F" + escapeFilePath(fwdirs[i]);
+ }
+ }
+ if(!project->isEmpty("QMAKE_RPATHDIR")) {
+ const QStringList &rpathdirs = project->values("QMAKE_RPATHDIR");
+ for(int i = 0; i < rpathdirs.size(); ++i) {
+ if(!project->isEmpty("QMAKE_LFLAGS_RPATH"))
+ project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + escapeFilePath(QFileInfo(rpathdirs[i]).absoluteFilePath());
+ }
+ }
+
+ project->values("QMAKE_FILETAGS") << "SOURCES" << "GENERATED_SOURCES" << "TARGET" << "DESTDIR";
+ if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(int i = 0; i < quc.size(); ++i)
+ project->values("QMAKE_FILETAGS") += project->values(quc[i]+".input");
+ }
+
+ if(project->isActiveConfig("GNUmake") && !project->isEmpty("QMAKE_CFLAGS_DEPS"))
+ include_deps = true; //do not generate deps
+ if(project->isActiveConfig("compile_libtool"))
+ Option::obj_ext = ".lo"; //override the .o
+
+ MakefileGenerator::init();
+
+ QString comps[] = { "C", "CXX", "OBJC", "OBJCXX", QString() };
+ for(int i = 0; !comps[i].isNull(); i++) {
+ QString compile_flag = var("QMAKE_COMPILE_FLAG");
+ if(compile_flag.isEmpty())
+ compile_flag = "-c";
+
+ if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
+ QString pchFlags = var("QMAKE_" + comps[i] + "FLAGS_USE_PRECOMPILE");
+
+ QString pchBaseName;
+ if(!project->isEmpty("PRECOMPILED_DIR")) {
+ pchBaseName = Option::fixPathToTargetOS(project->first("PRECOMPILED_DIR"));
+ if(!pchBaseName.endsWith(Option::dir_sep))
+ pchBaseName += Option::dir_sep;
+ }
+ pchBaseName += project->first("QMAKE_ORIG_TARGET");
+
+ // replace place holders
+ pchFlags = pchFlags.replace("${QMAKE_PCH_INPUT}",
+ fileFixify(project->first("PRECOMPILED_HEADER")));
+ pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT_BASE}", pchBaseName);
+ if (project->isActiveConfig("icc_pch_style")) {
+ // icc style
+ pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT}",
+ pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT"));
+ } else {
+ // gcc style (including clang_pch_style)
+ QString headerPrefix = project->first("QMAKE_PRECOMP_PREFIX");
+ QString headerSuffix;
+ if (project->isActiveConfig("clang_pch_style"))
+ headerSuffix = project->first("QMAKE_PCH_OUTPUT_EXT");
+ else
+ pchBaseName += project->first("QMAKE_PCH_OUTPUT_EXT");
+
+ pchBaseName += Option::dir_sep;
+ QString pchOutputFile;
+
+ if(comps[i] == "C") {
+ pchOutputFile = "c";
+ } else if(comps[i] == "CXX") {
+ pchOutputFile = "c++";
+ } else if(project->isActiveConfig("objective_c")) {
+ if(comps[i] == "OBJC")
+ pchOutputFile = "objective-c";
+ else if(comps[i] == "OBJCXX")
+ pchOutputFile = "objective-c++";
+ }
+
+ if(!pchOutputFile.isEmpty()) {
+ pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT}",
+ pchBaseName + pchOutputFile + headerSuffix);
+ }
+ }
+
+ if (!pchFlags.isEmpty())
+ compile_flag += " " + pchFlags;
+ }
+
+ QString cflags;
+ if(comps[i] == "OBJC" || comps[i] == "OBJCXX")
+ cflags += " $(CFLAGS)";
+ else
+ cflags += " $(" + comps[i] + "FLAGS)";
+ compile_flag += cflags + " $(INCPATH)";
+
+ QString compiler = comps[i];
+ if (compiler == "C")
+ compiler = "CC";
+
+ QString runComp = "QMAKE_RUN_" + compiler;
+ if(project->isEmpty(runComp))
+ project->values(runComp).append("$(" + compiler + ") " + compile_flag + " -o $obj $src");
+ QString runCompImp = "QMAKE_RUN_" + compiler + "_IMP";
+ if(project->isEmpty(runCompImp))
+ project->values(runCompImp).append("$(" + compiler + ") " + compile_flag + " -o \"$@\" \"$<\"");
+ }
+
+ if(project->isActiveConfig("macx") && !project->isEmpty("TARGET") && !project->isActiveConfig("compile_libtool") &&
+ ((project->isActiveConfig("build_pass") || project->isEmpty("BUILDS")))) {
+ QString bundle;
+ if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
+ bundle = unescapeFilePath(project->first("TARGET"));
+ if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
+ bundle = unescapeFilePath(project->first("QMAKE_BUNDLE_NAME"));
+ if(!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
+ bundle += project->first("QMAKE_BUNDLE_EXTENSION");
+ } else if(project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) {
+ bundle = unescapeFilePath(project->first("TARGET"));
+ if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
+ bundle = unescapeFilePath(project->first("QMAKE_APPLICATION_BUNDLE_NAME"));
+ if(!bundle.endsWith(".app"))
+ bundle += ".app";
+ if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
+ project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
+ project->values("QMAKE_PKGINFO").append(project->first("DESTDIR") + bundle + "/Contents/PkgInfo");
+ project->values("QMAKE_BUNDLE_RESOURCE_FILE").append(project->first("DESTDIR") + bundle + "/Contents/Resources/empty.lproj");
+ } else if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
+ ((!project->isActiveConfig("plugin") && project->isActiveConfig("lib_bundle")) ||
+ (project->isActiveConfig("plugin") && project->isActiveConfig("plugin_bundle")))) {
+ bundle = unescapeFilePath(project->first("TARGET"));
+ if(project->isActiveConfig("plugin")) {
+ if(!project->isEmpty("QMAKE_PLUGIN_BUNDLE_NAME"))
+ bundle = unescapeFilePath(project->first("QMAKE_PLUGIN_BUNDLE_NAME"));
+ if(!project->isEmpty("QMAKE_BUNDLE_EXTENSION") && !bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
+ bundle += project->first("QMAKE_BUNDLE_EXTENSION");
+ else if(!bundle.endsWith(".plugin"))
+ bundle += ".plugin";
+ if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
+ project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
+ } else {
+ if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
+ bundle = unescapeFilePath(project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"));
+ if(!project->isEmpty("QMAKE_BUNDLE_EXTENSION") && !bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
+ bundle += project->first("QMAKE_BUNDLE_EXTENSION");
+ else if(!bundle.endsWith(".framework"))
+ bundle += ".framework";
+ }
+ }
+ if(!bundle.isEmpty()) {
+ project->values("QMAKE_BUNDLE") = QStringList(bundle);
+ project->values("ALL_DEPS") += project->first("QMAKE_PKGINFO");
+ project->values("ALL_DEPS") += project->first("QMAKE_BUNDLE_RESOURCE_FILE");
+ } else {
+ project->values("QMAKE_BUNDLE").clear();
+ project->values("QMAKE_BUNDLE_LOCATION").clear();
+ }
+ } else { //no bundling here
+ project->values("QMAKE_BUNDLE").clear();
+ project->values("QMAKE_BUNDLE_LOCATION").clear();
+ }
+
+ if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES"))
+ project->values("DISTFILES") += project->values("QMAKE_INTERNAL_INCLUDED_FILES");
+ project->values("DISTFILES") += project->projectFile();
+
+ init2();
+ project->values("QMAKE_INTERNAL_PRL_LIBS") << "QMAKE_LIBDIR_FLAGS" << "QMAKE_FRAMEWORKPATH_FLAGS" << "QMAKE_LIBS";
+ if(!project->isEmpty("QMAKE_MAX_FILES_PER_AR")) {
+ bool ok;
+ int max_files = project->first("QMAKE_MAX_FILES_PER_AR").toInt(&ok);
+ QStringList ar_sublibs, objs = project->values("OBJECTS");
+ if(ok && max_files > 5 && max_files < (int)objs.count()) {
+ QString lib;
+ for(int i = 0, obj_cnt = 0, lib_cnt = 0; i != objs.size(); ++i) {
+ if((++obj_cnt) >= max_files) {
+ if(lib_cnt) {
+ lib.sprintf("lib%s-tmp%d.a",
+ project->first("QMAKE_ORIG_TARGET").toLatin1().constData(), lib_cnt);
+ ar_sublibs << lib;
+ obj_cnt = 0;
+ }
+ lib_cnt++;
+ }
+ }
+ }
+ if(!ar_sublibs.isEmpty()) {
+ project->values("QMAKE_AR_SUBLIBS") = ar_sublibs;
+ project->values("QMAKE_INTERNAL_PRL_LIBS") << "QMAKE_AR_SUBLIBS";
+ }
+ }
+
+ if(project->isActiveConfig("compile_libtool")) {
+ const QString libtoolify[] = { "QMAKE_RUN_CC", "QMAKE_RUN_CC_IMP",
+ "QMAKE_RUN_CXX", "QMAKE_RUN_CXX_IMP",
+ "QMAKE_LINK_THREAD", "QMAKE_LINK", "QMAKE_AR_CMD", "QMAKE_LINK_SHLIB_CMD",
+ QString() };
+ for(int i = 0; !libtoolify[i].isNull(); i++) {
+ QStringList &l = project->values(libtoolify[i]);
+ if(!l.isEmpty()) {
+ QString libtool_flags, comp_flags;
+ if(libtoolify[i].startsWith("QMAKE_LINK") || libtoolify[i] == "QMAKE_AR_CMD") {
+ libtool_flags += " --mode=link";
+ if(project->isActiveConfig("staticlib")) {
+ libtool_flags += " -static";
+ } else {
+ if(!project->isEmpty("QMAKE_LIB_FLAG")) {
+ int maj = project->first("VER_MAJ").toInt();
+ int min = project->first("VER_MIN").toInt();
+ int pat = project->first("VER_PAT").toInt();
+ comp_flags += " -version-info " + QString::number(10*maj + min) +
+ ":" + QString::number(pat) + ":0";
+ if(libtoolify[i] != "QMAKE_AR_CMD") {
+ QString rpath = Option::output_dir;
+ if(!project->isEmpty("DESTDIR")) {
+ rpath = project->first("DESTDIR");
+ if(QDir::isRelativePath(rpath))
+ rpath.prepend(Option::output_dir + Option::dir_sep);
+ }
+ comp_flags += " -rpath " + Option::fixPathToTargetOS(rpath, false);
+ }
+ }
+ }
+ if(project->isActiveConfig("plugin"))
+ libtool_flags += " -module";
+ } else {
+ libtool_flags += " --mode=compile";
+ }
+ l.first().prepend("$(LIBTOOL)" + libtool_flags + " ");
+ if(!comp_flags.isEmpty())
+ l.first() += comp_flags;
+ }
+ }
+ }
+}
+
+void
+UnixMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l)
+{
+ if(var == "QMAKE_PRL_LIBS") {
+ project->values("QMAKE_CURRENT_PRL_LIBS") += l;
+ } else
+ MakefileGenerator::processPrlVariable(var, l);
+}
+
+QStringList
+&UnixMakefileGenerator::findDependencies(const QString &file)
+{
+ QStringList &ret = MakefileGenerator::findDependencies(file);
+ // Note: The QMAKE_IMAGE_COLLECTION file have all images
+ // as dependency, so don't add precompiled header then
+ if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")
+ && file != project->first("QMAKE_IMAGE_COLLECTION")) {
+ QString header_prefix;
+ if(!project->isEmpty("PRECOMPILED_DIR"))
+ header_prefix = project->first("PRECOMPILED_DIR");
+ header_prefix += project->first("QMAKE_ORIG_TARGET");
+ if (!project->isActiveConfig("clang_pch_style"))
+ header_prefix += project->first("QMAKE_PCH_OUTPUT_EXT");
+ if (project->isActiveConfig("icc_pch_style")) {
+ // icc style
+ for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
+ if(file.endsWith(*it)) {
+ ret += header_prefix;
+ break;
+ }
+ }
+ } else {
+ // gcc style (including clang_pch_style)
+ QString header_suffix = project->isActiveConfig("clang_pch_style")
+ ? project->first("QMAKE_PCH_OUTPUT_EXT") : "";
+ header_prefix += Option::dir_sep + project->first("QMAKE_PRECOMP_PREFIX");
+ for(QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
+ if(file.endsWith(*it)) {
+ if(!project->isEmpty("QMAKE_CFLAGS_PRECOMPILE")) {
+ QString precomp_c_h = header_prefix + "c" + header_suffix;
+ if(!ret.contains(precomp_c_h))
+ ret += precomp_c_h;
+ }
+ if(project->isActiveConfig("objective_c")) {
+ if(!project->isEmpty("QMAKE_OBJCFLAGS_PRECOMPILE")) {
+ QString precomp_objc_h = header_prefix + "objective-c" + header_suffix;
+ if(!ret.contains(precomp_objc_h))
+ ret += precomp_objc_h;
+ }
+ if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) {
+ QString precomp_objcpp_h = header_prefix + "objective-c++" + header_suffix;
+ if(!ret.contains(precomp_objcpp_h))
+ ret += precomp_objcpp_h;
+ }
+ }
+ break;
+ }
+ }
+ for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
+ if(file.endsWith(*it)) {
+ if(!project->isEmpty("QMAKE_CXXFLAGS_PRECOMPILE")) {
+ QString precomp_cpp_h = header_prefix + "c++" + header_suffix;
+ if(!ret.contains(precomp_cpp_h))
+ ret += precomp_cpp_h;
+ }
+ if(project->isActiveConfig("objective_c")) {
+ if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) {
+ QString precomp_objcpp_h = header_prefix + "objective-c++" + header_suffix;
+ if(!ret.contains(precomp_objcpp_h))
+ ret += precomp_objcpp_h;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+bool
+UnixMakefileGenerator::findLibraries()
+{
+ QList<QMakeLocalFileName> libdirs, frameworkdirs;
+ frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks"));
+ frameworkdirs.append(QMakeLocalFileName("/Library/Frameworks"));
+ const QString lflags[] = { "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LFLAGS", "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", QString() };
+ for(int i = 0; !lflags[i].isNull(); i++) {
+ QStringList &l = project->values(lflags[i]);
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ bool do_suffix = true;
+ QString stub, dir, extn, opt = (*it).trimmed();
+ if(opt.startsWith("-")) {
+ if(opt.startsWith("-L")) {
+ QMakeLocalFileName f(opt.right(opt.length()-2));
+ if(!libdirs.contains(f))
+ libdirs.append(f);
+ } else if(opt.startsWith("-l")) {
+ if (!project->isEmpty("QMAKE_RVCT_LINKSTYLE")) {
+ (*it) = opt.mid(2);
+ } else if (project->isActiveConfig("rvct_linker") || project->isActiveConfig("armcc_linker")) {
+ (*it) = "lib" + opt.mid(2) + ".so";
+ } else {
+ stub = opt.mid(2);
+ }
+ } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F")) {
+ frameworkdirs.append(QMakeLocalFileName(opt.right(opt.length()-2)));
+ } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) {
+ if(opt.length() > 11) {
+ opt = opt.mid(11);
+ } else {
+ ++it;
+ opt = (*it);
+ }
+ do_suffix = false;
+ extn = "";
+ dir = "/System/Library/Frameworks/" + opt + ".framework/";
+ stub = opt;
+ }
+ } else {
+ extn = dir = "";
+ stub = opt;
+ int slsh = opt.lastIndexOf(Option::dir_sep);
+ if(slsh != -1) {
+ dir = opt.left(slsh);
+ stub = opt.mid(slsh+1);
+ }
+ QRegExp stub_reg("^.*lib(" + stub + "[^./=]*)\\.(.*)$");
+ if(stub_reg.exactMatch(stub)) {
+ stub = stub_reg.cap(1);
+ extn = stub_reg.cap(2);
+ }
+ }
+ if(!stub.isEmpty()) {
+ if(do_suffix && !project->isEmpty("QMAKE_" + stub.toUpper() + "_SUFFIX"))
+ stub += project->first("QMAKE_" + stub.toUpper() + "_SUFFIX");
+ bool found = false;
+ QStringList extens;
+ if(!extn.isNull())
+ extens << extn;
+ else if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB"))
+ // In Symbian you link to the stub .lib file, but run with the .dll file.
+ extens << "lib";
+ else
+ extens << project->values("QMAKE_EXTENSION_SHLIB").first() << "a";
+ for(QStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) {
+ if(dir.isNull()) {
+ for(QList<QMakeLocalFileName>::Iterator dep_it = libdirs.begin(); dep_it != libdirs.end(); ++dep_it) {
+ QString pathToLib = ((*dep_it).local() + Option::dir_sep
+ + project->values("QMAKE_PREFIX_SHLIB").first()
+ + stub + "." + (*extit));
+ if(exists(pathToLib)) {
+ if (!project->isEmpty("QMAKE_RVCT_LINKSTYLE"))
+ (*it) = pathToLib;
+ else
+ (*it) = "-l" + stub;
+ found = true;
+ break;
+ }
+ }
+ } else {
+ if(exists(project->values("QMAKE_PREFIX_SHLIB").first() + stub + "." + (*extit))) {
+ (*it) = project->values("QMAKE_PREFIX_SHLIB").first() + stub + "." + (*extit);
+ found = true;
+ break;
+ }
+ }
+ }
+ if(!found && project->isActiveConfig("compile_libtool")) {
+ for(int dep_i = 0; dep_i < libdirs.size(); ++dep_i) {
+ if(exists(libdirs[dep_i].local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + stub + Option::libtool_ext)) {
+ (*it) = libdirs[dep_i].real() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + stub + Option::libtool_ext;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+QString linkLib(const QString &file, const QString &libName) {
+ QString ret;
+ QRegExp reg("^.*lib(" + QRegExp::escape(libName) + "[^./=]*).*$");
+ if(reg.exactMatch(file))
+ ret = "-l" + reg.cap(1);
+ return ret;
+}
+
+void
+UnixMakefileGenerator::processPrlFiles()
+{
+ QList<QMakeLocalFileName> libdirs, frameworkdirs;
+ frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks"));
+ const QString lflags[] = { "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LFLAGS", "QMAKE_LIBS", QString() };
+ for(int i = 0; !lflags[i].isNull(); i++) {
+ QStringList &l = project->values(lflags[i]);
+ for(int lit = 0; lit < l.size(); ++lit) {
+ QString opt = l.at(lit).trimmed();
+ if(opt.startsWith("-")) {
+ if(opt.startsWith("-L")) {
+ QMakeLocalFileName l(opt.right(opt.length()-2));
+ if(!libdirs.contains(l))
+ libdirs.append(l);
+ } else if(opt.startsWith("-l")) {
+ QString lib = opt.right(opt.length() - 2);
+ for(int dep_i = 0; dep_i < libdirs.size(); ++dep_i) {
+ const QMakeLocalFileName &lfn = libdirs[dep_i];
+ if(!project->isActiveConfig("compile_libtool")) { //give them the .libs..
+ QString la = lfn.local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + lib + Option::libtool_ext;
+ if(exists(la) && QFile::exists(lfn.local() + Option::dir_sep + ".libs")) {
+ QString dot_libs = lfn.real() + Option::dir_sep + ".libs";
+ l.append("-L" + dot_libs);
+ libdirs.append(QMakeLocalFileName(dot_libs));
+ }
+ }
+
+ QString prl = lfn.local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + lib;
+ if(!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX"))
+ prl += project->first("QMAKE_" + lib.toUpper() + "_SUFFIX");
+ if(processPrlFile(prl)) {
+ if(prl.startsWith(lfn.local()))
+ prl.replace(0, lfn.local().length(), lfn.real());
+ opt = linkLib(prl, lib);
+ break;
+ }
+ }
+ } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F")) {
+ QMakeLocalFileName f(opt.right(opt.length()-2));
+ if(!frameworkdirs.contains(f))
+ frameworkdirs.append(f);
+ } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) {
+ if(opt.length() > 11)
+ opt = opt.mid(11);
+ else
+ opt = l.at(++lit);
+ opt = opt.trimmed();
+ const QList<QMakeLocalFileName> dirs = frameworkdirs + libdirs;
+ for(int dep_i = 0; dep_i < dirs.size(); ++dep_i) {
+ QString prl = dirs[dep_i].local() + "/" + opt + ".framework/" + opt + Option::prl_ext;
+ if(processPrlFile(prl))
+ break;
+ }
+ }
+ } else if(!opt.isNull()) {
+ QString lib = opt;
+ processPrlFile(lib);
+#if 0
+ if(ret)
+ opt = linkLib(lib, "");
+#endif
+ if(!opt.isEmpty())
+ l.replaceInStrings(lib, opt);
+ }
+
+ QStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
+ if(!prl_libs.isEmpty()) {
+ for(int prl = 0; prl < prl_libs.size(); ++prl)
+ l.insert(lit+prl+1, prl_libs.at(prl));
+ prl_libs.clear();
+ }
+ }
+
+ //merge them into a logical order
+ if(!project->isActiveConfig("no_smart_library_merge") && !project->isActiveConfig("no_lflags_merge")) {
+ QHash<QString, QStringList> lflags;
+ for(int lit = 0; lit < l.size(); ++lit) {
+ QString arch("default");
+ QString opt = l.at(lit).trimmed();
+ if(opt.startsWith("-")) {
+ if (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-Xarch")) {
+ if (opt.length() > 7) {
+ arch = opt.mid(7);
+ opt = l.at(++lit);
+ }
+ }
+
+ if(opt.startsWith("-L") ||
+ (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F"))) {
+ if(!lflags[arch].contains(opt))
+ lflags[arch].append(opt);
+ } else if(opt.startsWith("-l") || opt == "-pthread") {
+ // Make sure we keep the dependency-order of libraries
+ if (lflags[arch].contains(opt))
+ lflags[arch].removeAll(opt);
+ lflags[arch].append(opt);
+ } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) {
+ if(opt.length() > 11)
+ opt = opt.mid(11);
+ else {
+ opt = l.at(++lit);
+ if (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-Xarch"))
+ opt = l.at(++lit); // The user has done the right thing and prefixed each part
+ }
+ bool found = false;
+ for(int x = 0; x < lflags[arch].size(); ++x) {
+ QString xf = lflags[arch].at(x);
+ if(xf.startsWith("-framework")) {
+ QString framework;
+ if(xf.length() > 11)
+ framework = xf.mid(11);
+ else
+ framework = lflags[arch].at(++x);
+ if(framework == opt) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if(!found) {
+ lflags[arch].append("-framework");
+ lflags[arch].append(opt);
+ }
+ } else {
+ lflags[arch].append(opt);
+ }
+ } else if(!opt.isNull()) {
+ if(!lflags[arch].contains(opt))
+ lflags[arch].append(opt);
+ }
+ }
+
+ l = lflags.take("default");
+
+ // Process architecture specific options (Xarch)
+ QHash<QString, QStringList>::const_iterator archIterator = lflags.constBegin();
+ while (archIterator != lflags.constEnd()) {
+ const QStringList archOptions = archIterator.value();
+ for (int i = 0; i < archOptions.size(); ++i) {
+ l.append(QLatin1String("-Xarch_") + archIterator.key());
+ l.append(archOptions.at(i));
+ }
+ ++archIterator;
+ }
+ }
+ }
+}
+
+QString
+UnixMakefileGenerator::defaultInstall(const QString &t)
+{
+ if(t != "target" || project->first("TEMPLATE") == "subdirs")
+ return QString();
+
+ bool bundle = false;
+ const QString root = "$(INSTALL_ROOT)";
+ QStringList &uninst = project->values(t + ".uninstall");
+ QString ret, destdir=project->first("DESTDIR");
+ QString targetdir = Option::fixPathToTargetOS(project->first("target.path"), false);
+ if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep)
+ destdir += Option::dir_sep;
+ targetdir = fileFixify(targetdir, FileFixifyAbsolute);
+ if(targetdir.right(1) != Option::dir_sep)
+ targetdir += Option::dir_sep;
+
+ QStringList links;
+ QString target="$(TARGET)";
+ QStringList &targets = project->values(t + ".targets");
+ if(!project->isEmpty("QMAKE_BUNDLE")) {
+ target = project->first("QMAKE_BUNDLE");
+ bundle = true;
+ } else if(project->first("TEMPLATE") == "app") {
+ target = "$(QMAKE_TARGET)";
+ } else if(project->first("TEMPLATE") == "lib") {
+ if(project->isEmpty("QMAKE_CYGWIN_SHLIB")) {
+ if(!project->isActiveConfig("staticlib") && !project->isActiveConfig("plugin")) {
+ if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
+ links << "$(TARGET0)" << "$(TARGET1)" << "$(TARGET2)";
+ } else {
+ links << "$(TARGET0)";
+ }
+ }
+ }
+ }
+ for(int i = 0; i < targets.size(); ++i) {
+ QString src = targets.at(i),
+ dst = filePrefixRoot(root, targetdir + src.section('/', -1));
+ if(!ret.isEmpty())
+ ret += "\n\t";
+ ret += "-$(INSTALL_FILE) \"" + src + "\" \"" + dst + "\"";
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append("-$(DEL_FILE) \"" + dst + "\"");
+ }
+
+ if(!bundle && project->isActiveConfig("compile_libtool")) {
+ QString src_targ = target;
+ if(src_targ == "$(TARGET)")
+ src_targ = "$(TARGETL)";
+ QString dst_dir = fileFixify(targetdir, FileFixifyAbsolute);
+ if(QDir::isRelativePath(dst_dir))
+ dst_dir = Option::fixPathToTargetOS(Option::output_dir + Option::dir_sep + dst_dir);
+ ret = "-$(LIBTOOL) --mode=install cp \"" + src_targ + "\" \"" + filePrefixRoot(root, dst_dir) + "\"";
+ uninst.append("-$(LIBTOOL) --mode=uninstall \"" + src_targ + "\"");
+ } else {
+ QString src_targ = target;
+ if(!destdir.isEmpty())
+ src_targ = Option::fixPathToTargetOS(destdir + target, false);
+ QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + target, FileFixifyAbsolute));
+ if(bundle) {
+ if(!ret.isEmpty())
+ ret += "\n\t";
+ ret += "$(DEL_FILE) -r \"" + dst_targ + "\"\n\t";
+ }
+ if(!ret.isEmpty())
+ ret += "\n\t";
+
+ QString copy_cmd("-");
+ if (bundle)
+ copy_cmd += "$(INSTALL_DIR)";
+ else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib"))
+ copy_cmd += "$(INSTALL_FILE)";
+ else
+ copy_cmd += "$(INSTALL_PROGRAM)";
+ copy_cmd += " \"" + src_targ + "\" \"" + dst_targ + "\"";
+ if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib")
+ && project->values(t + ".CONFIG").indexOf("fix_rpath") != -1) {
+ if(!project->isEmpty("QMAKE_FIX_RPATH")) {
+ ret += copy_cmd;
+ ret += "\n\t-" + var("QMAKE_FIX_RPATH") + " \"" +
+ dst_targ + "\" \"" + dst_targ + "\"";
+ } else if(!project->isEmpty("QMAKE_LFLAGS_RPATH")) {
+ ret += "-$(LINK) $(LFLAGS) " + var("QMAKE_LFLAGS_RPATH") + targetdir + " -o \"" +
+ dst_targ + "\" $(OBJECTS) $(LIBS) $(OBJCOMP)";
+ } else {
+ ret += copy_cmd;
+ }
+ } else {
+ ret += copy_cmd;
+ }
+
+ if(project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
+ if(!project->isEmpty("QMAKE_RANLIB"))
+ ret += QString("\n\t$(RANLIB) \"") + dst_targ + "\"";
+ } else if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") && !project->isEmpty("QMAKE_STRIP")) {
+ ret += "\n\t-$(STRIP)";
+ if(project->first("TEMPLATE") == "lib" && !project->isEmpty("QMAKE_STRIPFLAGS_LIB"))
+ ret += " " + var("QMAKE_STRIPFLAGS_LIB");
+ else if(project->first("TEMPLATE") == "app" && !project->isEmpty("QMAKE_STRIPFLAGS_APP"))
+ ret += " " + var("QMAKE_STRIPFLAGS_APP");
+ if(bundle)
+ ret = " \"" + dst_targ + "/Contents/MacOS/$(QMAKE_TARGET)\"";
+ else
+ ret += " \"" + dst_targ + "\"";
+ }
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ if(bundle)
+ uninst.append("-$(DEL_FILE) -r \"" + dst_targ + "\"");
+ else
+ uninst.append("-$(DEL_FILE) \"" + dst_targ + "\"");
+ if(!links.isEmpty()) {
+ for(int i = 0; i < links.size(); ++i) {
+ if(Option::target_mode == Option::TARG_UNIX_MODE ||
+ Option::target_mode == Option::TARG_MACX_MODE) {
+ QString link = Option::fixPathToTargetOS(destdir + links[i], false);
+ int lslash = link.lastIndexOf(Option::dir_sep);
+ if(lslash != -1)
+ link = link.right(link.length() - (lslash + 1));
+ QString dst_link = filePrefixRoot(root, fileFixify(targetdir + link, FileFixifyAbsolute));
+ ret += "\n\t-$(SYMLINK) \"$(TARGET)\" \"" + dst_link + "\"";
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append("-$(DEL_FILE) \"" + dst_link + "\"");
+ }
+ }
+ }
+ }
+ if(project->first("TEMPLATE") == "lib") {
+ QStringList types;
+ types << "prl" << "libtool" << "pkgconfig";
+ for(int i = 0; i < types.size(); ++i) {
+ const QString type = types.at(i);
+ QString meta;
+ if(type == "prl" && project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
+ !project->isEmpty("QMAKE_INTERNAL_PRL_FILE"))
+ meta = prlFileName(false);
+ if(type == "libtool" && project->isActiveConfig("create_libtool") && !project->isActiveConfig("compile_libtool"))
+ meta = libtoolFileName(false);
+ if(type == "pkgconfig" && project->isActiveConfig("create_pc"))
+ meta = pkgConfigFileName(false);
+ if(!meta.isEmpty()) {
+ QString src_meta = meta;
+ if(!destdir.isEmpty())
+ src_meta = Option::fixPathToTargetOS(destdir + meta, false);
+ QString dst_meta = filePrefixRoot(root, fileFixify(targetdir + meta, FileFixifyAbsolute));
+ if(!uninst.isEmpty())
+ uninst.append("\n\t");
+ uninst.append("-$(DEL_FILE) \"" + dst_meta + "\"");
+ const QString replace_rule("QMAKE_" + type.toUpper() + "_INSTALL_REPLACE");
+ const QString dst_meta_dir = fileInfo(dst_meta).path();
+ if(!dst_meta_dir.isEmpty()) {
+ if(!ret.isEmpty())
+ ret += "\n\t";
+ ret += mkdir_p_asstring(dst_meta_dir, true);
+ }
+ QString install_meta = "$(INSTALL_FILE) \"" + src_meta + "\" \"" + dst_meta + "\"";
+ if(project->isEmpty(replace_rule) || project->isActiveConfig("no_sed_meta_install")) {
+ if(!ret.isEmpty())
+ ret += "\n\t";
+ ret += "-" + install_meta;
+ } else {
+ if(!ret.isEmpty())
+ ret += "\n\t";
+ ret += "-$(SED)";
+ QStringList replace_rules = project->values(replace_rule);
+ for(int r = 0; r < replace_rules.size(); ++r) {
+ const QString match = project->first(replace_rules.at(r) + ".match"),
+ replace = project->first(replace_rules.at(r) + ".replace");
+ if(!match.isEmpty() /*&& match != replace*/)
+ ret += " -e \"s," + match + "," + replace + ",g\"";
+ }
+ ret += " \"" + src_meta + "\" >\"" + dst_meta + "\"";
+ //ret += " || " + install_meta;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+QString
+UnixMakefileGenerator::escapeFilePath(const QString &path) const
+{
+ QString ret = path;
+ if(!ret.isEmpty()) {
+ ret = unescapeFilePath(ret).replace(QLatin1Char(' '), QLatin1String("\\ "));
+ debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/qmake/generators/unix/unixmake.h b/qmake/generators/unix/unixmake.h
new file mode 100644
index 0000000000..acbe8c4a6b
--- /dev/null
+++ b/qmake/generators/unix/unixmake.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UNIXMAKE_H
+#define UNIXMAKE_H
+
+#include "makefile.h"
+
+QT_BEGIN_NAMESPACE
+
+class UnixMakefileGenerator : public MakefileGenerator
+{
+ bool init_flag, include_deps;
+ QString libtoolFileName(bool fixify=true);
+ void writeLibtoolFile(); // for libtool
+ void writePrlFile(QTextStream &);
+
+public:
+ UnixMakefileGenerator();
+ ~UnixMakefileGenerator();
+
+protected:
+ virtual bool doPrecompiledHeaders() const { return project->isActiveConfig("precompile_header"); }
+ virtual bool doDepends() const { return !include_deps && !Option::mkfile::do_stub_makefile && MakefileGenerator::doDepends(); }
+ virtual QString defaultInstall(const QString &);
+ virtual void processPrlVariable(const QString &, const QStringList &);
+ virtual void processPrlFiles();
+
+ virtual bool findLibraries();
+ virtual QString escapeFilePath(const QString &path) const;
+ virtual QStringList &findDependencies(const QString &);
+ virtual void init();
+
+ void writeMakeParts(QTextStream &);
+ bool writeMakefile(QTextStream &);
+
+private:
+ void init2();
+};
+
+inline UnixMakefileGenerator::~UnixMakefileGenerator()
+{ }
+
+QT_END_NAMESPACE
+
+#endif // UNIXMAKE_H
diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp
new file mode 100644
index 0000000000..9f144929ed
--- /dev/null
+++ b/qmake/generators/unix/unixmake2.cpp
@@ -0,0 +1,1352 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "unixmake.h"
+#include "option.h"
+#include "meta.h"
+#include <qregexp.h>
+#include <qbytearray.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <time.h>
+
+QT_BEGIN_NAMESPACE
+
+UnixMakefileGenerator::UnixMakefileGenerator() : MakefileGenerator(), init_flag(false), include_deps(false)
+{
+
+}
+
+void
+UnixMakefileGenerator::writePrlFile(QTextStream &t)
+{
+ MakefileGenerator::writePrlFile(t);
+ // libtool support
+
+ if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib") { //write .la
+ if(project->isActiveConfig("compile_libtool"))
+ warn_msg(WarnLogic, "create_libtool specified with compile_libtool can lead to conflicting .la\n"
+ "formats, create_libtool has been disabled\n");
+ else
+ writeLibtoolFile();
+ }
+ // pkg-config support
+ if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib")
+ writePkgConfigFile();
+}
+
+bool
+UnixMakefileGenerator::writeMakefile(QTextStream &t)
+{
+
+ writeHeader(t);
+ if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ QStringList &qut = project->values("QMAKE_EXTRA_TARGETS");
+ for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it)
+ t << *it << " ";
+ t << "first all clean install distclean uninstall qmake_all:" << "\n\t"
+ << "@echo \"Some of the required modules ("
+ << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t"
+ << "@echo \"Skipped.\"" << endl << endl;
+ writeMakeQmake(t);
+ if(project->isEmpty("QMAKE_NOFORCE"))
+ t << "FORCE:" << endl << endl;
+ return true;
+ }
+
+ if (project->values("TEMPLATE").first() == "app" ||
+ project->values("TEMPLATE").first() == "lib") {
+ if(Option::mkfile::do_stub_makefile && MakefileGenerator::writeStubMakefile(t))
+ return true;
+ writeMakeParts(t);
+ return MakefileGenerator::writeMakefile(t);
+ } else if(project->values("TEMPLATE").first() == "subdirs") {
+ MakefileGenerator::writeSubDirs(t);
+ return true;
+ }
+ return false;
+}
+
+void
+UnixMakefileGenerator::writeMakeParts(QTextStream &t)
+{
+ QString deps = fileFixify(Option::output.fileName()), target_deps, prl;
+ bool do_incremental = (project->isActiveConfig("incremental") &&
+ !project->values("QMAKE_INCREMENTAL").isEmpty() &&
+ (!project->values("QMAKE_APP_FLAG").isEmpty() ||
+ (!project->isActiveConfig("staticlib")))),
+ src_incremental=false;
+
+ t << "####### Compiler, tools and options" << endl << endl;
+ t << "CC = " << var("QMAKE_CC") << endl;
+ t << "CXX = " << var("QMAKE_CXX") << endl;
+ t << "DEFINES = "
+ << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
+ << varGlue("DEFINES","-D"," -D","") << endl;
+ t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)" << endl;
+ t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)" << endl;
+ t << "INCPATH = " << "-I" << specdir();
+ if(!project->isActiveConfig("no_include_pwd")) {
+ QString pwd = escapeFilePath(fileFixify(qmake_getpwd()));
+ if(pwd.isEmpty())
+ pwd = ".";
+ t << " -I" << pwd;
+ }
+ {
+ const QStringList &incs = project->values("INCLUDEPATH");
+ for(int i = 0; i < incs.size(); ++i) {
+ QString inc = escapeFilePath(incs.at(i));
+ if(!inc.isEmpty())
+ t << " " << "-I" << inc;
+ }
+ }
+ if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS"))
+ t << " " << var("QMAKE_FRAMEWORKPATH_FLAGS");
+ t << endl;
+
+ if(!project->isActiveConfig("staticlib")) {
+ t << "LINK = " << var("QMAKE_LINK") << endl;
+ t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl;
+ t << "LIBS = " << "$(SUBLIBS) " << var("QMAKE_FRAMEWORKPATH_FLAGS") << " "
+ << var("QMAKE_LIBDIR_FLAGS") << " " << var("QMAKE_LIBS") << " " << var("QMAKE_LIBS_PRIVATE") << endl;
+ }
+
+ t << "AR = " << var("QMAKE_AR") << endl;
+ t << "RANLIB = " << var("QMAKE_RANLIB") << endl;
+ t << "QMAKE = " << var("QMAKE_QMAKE") << endl;
+ t << "TAR = " << var("QMAKE_TAR") << endl;
+ t << "COMPRESS = " << var("QMAKE_GZIP") << endl;
+ if(project->isActiveConfig("compile_libtool"))
+ t << "LIBTOOL = " << var("QMAKE_LIBTOOL") << endl;
+ t << "COPY = " << var("QMAKE_COPY") << endl;
+ t << "SED = " << var("QMAKE_STREAM_EDITOR") << endl;
+ t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl;
+ t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl;
+ t << "STRIP = " << var("QMAKE_STRIP") << endl;
+ t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl;
+ t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl;
+ t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl;
+
+ t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl;
+ t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl;
+ t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl;
+ t << "MOVE = " << var("QMAKE_MOVE") << endl;
+ t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl;
+ t << "MKDIR = " << var("QMAKE_MKDIR") << endl;
+ if(!project->isEmpty("QMAKE_MACOSX_DEPLOYMENT_TARGET"))
+ t << "export MACOSX_DEPLOYMENT_TARGET = " //exported to children processes
+ << project->first("QMAKE_MACOSX_DEPLOYMENT_TARGET") << endl;
+
+ if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) {
+ t << "vpath %.dso " << project->values("QMAKE_LIBDIR").join(":") << endl;
+ t << "vpath %.lib " << project->values("QMAKE_LIBDIR").join(":") << endl;
+ }
+
+ t << endl;
+
+ t << "####### Output directory" << endl << endl;
+ if (! project->values("OBJECTS_DIR").isEmpty())
+ t << "OBJECTS_DIR = " << var("OBJECTS_DIR") << endl;
+ else
+ t << "OBJECTS_DIR = ./" << endl;
+ t << endl;
+
+ /* files */
+ t << "####### Files" << endl << endl;
+ t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES"))) << " "
+ << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << endl;
+ if(do_incremental) {
+ QStringList &objs = project->values("OBJECTS"), &incrs = project->values("QMAKE_INCREMENTAL"), incrs_out;
+ t << "OBJECTS = ";
+ for(QStringList::Iterator objit = objs.begin(); objit != objs.end(); ++objit) {
+ bool increment = false;
+ for(QStringList::Iterator incrit = incrs.begin(); incrit != incrs.end(); ++incrit) {
+ if((*objit).indexOf(QRegExp((*incrit), Qt::CaseSensitive,
+ QRegExp::Wildcard)) != -1) {
+ increment = true;
+ incrs_out.append((*objit));
+ break;
+ }
+ }
+ if(!increment)
+ t << "\\\n\t\t" << (*objit);
+ }
+ if(incrs_out.count() == objs.count()) { //we just switched places, no real incrementals to be done!
+ t << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl;
+ } else if(!incrs_out.count()) {
+ t << endl;
+ } else {
+ src_incremental = true;
+ t << endl;
+ t << "INCREMENTAL_OBJECTS = " << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl;
+ }
+ } else {
+ t << "OBJECTS = " << valList(escapeFilePaths(project->values("OBJECTS"))) << endl;
+ }
+ if(do_incremental && !src_incremental)
+ do_incremental = false;
+ t << "DIST = " << valList(fileFixify(project->values("DISTFILES"))) << endl;
+ t << "QMAKE_TARGET = " << var("QMAKE_ORIG_TARGET") << endl;
+ t << "DESTDIR = " << var("DESTDIR") << endl;
+ if(project->isActiveConfig("compile_libtool"))
+ t << "TARGETL = " << var("TARGET_la") << endl;
+ t << "TARGET = " << escapeFilePath(var("TARGET")) << endl;
+ if(project->isActiveConfig("plugin")) {
+ t << "TARGETD = " << escapeFilePath(var("TARGET")) << endl;
+ } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty()) {
+ t << "TARGETA = " << escapeFilePath(var("TARGETA")) << endl;
+ if(!project->isEmpty("QMAKE_BUNDLE")) {
+ t << "TARGETD = " << escapeFilePath(var("TARGET_x.y")) << endl;
+ t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl;
+ } else if(!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) {
+ t << "TARGETD = " << escapeFilePath(var("TARGET")) << endl;
+ } else if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
+ t << "TARGETD = " << escapeFilePath(var("TARGET_x.y.z")) << endl;
+ t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl;
+ t << "TARGET1 = " << escapeFilePath(var("TARGET_x")) << endl;
+ t << "TARGET2 = " << escapeFilePath(var("TARGET_x.y")) << endl;
+ } else {
+ t << "TARGETD = " << escapeFilePath(var("TARGET_x")) << endl;
+ t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl;
+ }
+ }
+ writeExtraCompilerVariables(t);
+ writeExtraVariables(t);
+ t << endl;
+
+ // blasted includes
+ QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES");
+ QStringList::Iterator it;
+ for(it = qeui.begin(); it != qeui.end(); ++it)
+ t << "include " << (*it) << endl;
+
+ /* rules */
+ t << "first: all" << endl;
+ t << "####### Implicit rules" << endl << endl;
+ t << ".SUFFIXES: " << Option::obj_ext;
+ for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
+ t << " " << (*cit);
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
+ t << " " << (*cppit);
+ t << endl << endl;
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
+ t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << endl << endl;
+ for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
+ t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << endl << endl;
+
+ if(include_deps) {
+ QString cmd=var("QMAKE_CFLAGS_DEPS") + " ";
+ cmd += varGlue("DEFINES","-D"," -D","") + varGlue("PRL_EXPORT_DEFINES"," -D"," -D","");
+ if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH"))
+ cmd += " -I" + project->first("QMAKE_ABSOLUTE_SOURCE_PATH") + " ";
+ cmd += " $(INCPATH) " + varGlue("DEPENDPATH", "-I", " -I", "");
+ QString odir;
+ if(!project->values("OBJECTS_DIR").isEmpty())
+ odir = project->first("OBJECTS_DIR");
+ t << "###### Dependencies" << endl << endl;
+ t << odir << ".deps/%.d: %.cpp\n\t";
+ if(project->isActiveConfig("echo_depend_creation"))
+ t << "@echo Creating depend for $<" << "\n\t";
+ t << mkdir_p_asstring("$(@D)") << "\n\t"
+ << "@$(CXX) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@" << endl << endl;
+
+ t << odir << ".deps/%.d: %.c\n\t";
+ if(project->isActiveConfig("echo_depend_creation"))
+ t << "@echo Creating depend for $<" << "\n\t";
+ t << mkdir_p_asstring("$(@D)") << "\n\t"
+ << "@$(CC) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@" << endl << endl;
+
+ QString src[] = { "SOURCES", "GENERATED_SOURCES", QString() };
+ for(int x = 0; !src[x].isNull(); x++) {
+ QStringList &l = project->values(src[x]);
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if(!(*it).isEmpty()) {
+ QString d_file;
+ for(QStringList::Iterator cit = Option::c_ext.begin();
+ cit != Option::c_ext.end(); ++cit) {
+ if((*it).endsWith((*cit))) {
+ d_file = (*it).left((*it).length() - (*cit).length());
+ break;
+ }
+ }
+ if(d_file.isEmpty()) {
+ for(QStringList::Iterator cppit = Option::cpp_ext.begin();
+ cppit != Option::cpp_ext.end(); ++cppit) {
+ if((*it).endsWith((*cppit))) {
+ d_file = (*it).left((*it).length() - (*cppit).length());
+ break;
+ }
+ }
+ }
+ if(!d_file.isEmpty()) {
+ d_file = odir + ".deps/" + d_file + ".d";
+ QStringList deps = findDependencies((*it)).filter(QRegExp(Option::cpp_moc_ext + "$"));
+ if(!deps.isEmpty())
+ t << d_file << ": " << deps.join(" ") << endl;
+ t << "-include " << d_file << endl;
+ project->values("QMAKE_DISTCLEAN") += d_file;
+ }
+ }
+ }
+ }
+ }
+
+ t << "####### Build rules" << endl << endl;
+ if(!project->values("SUBLIBS").isEmpty()) {
+ QString libdir = "tmp/";
+ if(!project->isEmpty("SUBLIBS_DIR"))
+ libdir = project->first("SUBLIBS_DIR");
+ t << "SUBLIBS = ";
+ QStringList &l = project->values("SUBLIBS");
+ for(QStringList::Iterator it = l.begin(); it != l.end(); ++it)
+ t << libdir << project->first("QMAKE_PREFIX_STATICLIB") << (*it) << "."
+ << project->first("QMAKE_EXTENSION_STATICLIB") << " ";
+ t << endl << endl;
+ }
+ if(project->isActiveConfig("depend_prl") && !project->isEmpty("QMAKE_PRL_INTERNAL_FILES")) {
+ QStringList &l = project->values("QMAKE_PRL_INTERNAL_FILES");
+ QStringList::Iterator it;
+ for(it = l.begin(); it != l.end(); ++it) {
+ QMakeMetaInfo libinfo;
+ if(libinfo.readLib((*it)) && !libinfo.isEmpty("QMAKE_PRL_BUILD_DIR")) {
+ QString dir;
+ int slsh = (*it).lastIndexOf(Option::dir_sep);
+ if(slsh != -1)
+ dir = (*it).left(slsh + 1);
+ QString targ = dir + libinfo.first("QMAKE_PRL_TARGET");
+ target_deps += " " + targ;
+ t << targ << ":" << "\n\t"
+ << "@echo \"Creating '" << targ << "'\"" << "\n\t"
+ << "(cd " << libinfo.first("QMAKE_PRL_BUILD_DIR") << ";"
+ << "$(MAKE))" << endl;
+ }
+ }
+ }
+ if(!project->values("QMAKE_APP_FLAG").isEmpty()) {
+ QString destdir = project->first("DESTDIR");
+ if(!project->isEmpty("QMAKE_BUNDLE")) {
+ QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION");
+ if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/"))
+ bundle_loc.prepend("/");
+ if(!bundle_loc.endsWith("/"))
+ bundle_loc += "/";
+ destdir += project->first("QMAKE_BUNDLE") + bundle_loc;
+ }
+ if(do_incremental) {
+ //incremental target
+ QString incr_target = var("TARGET") + "_incremental";
+ if(incr_target.indexOf(Option::dir_sep) != -1)
+ incr_target = incr_target.right(incr_target.length() -
+ (incr_target.lastIndexOf(Option::dir_sep) + 1));
+ QString incr_deps, incr_objs;
+ if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") {
+ QString incr_target_dir = var("OBJECTS_DIR") + incr_target + Option::obj_ext;
+ //actual target
+ t << incr_target_dir << ": $(OBJECTS)" << "\n\t"
+ << "ld -r -o "<< incr_target_dir << " $(OBJECTS)" << endl;
+ //communicated below
+ deps.prepend(incr_target_dir + " ");
+ incr_deps = "$(INCREMENTAL_OBJECTS)";
+ if(!incr_objs.isEmpty())
+ incr_objs += " ";
+ incr_objs += incr_target_dir;
+ } else {
+ //actual target
+ QString incr_target_dir = var("DESTDIR") + "lib" + incr_target + "." +
+ project->values("QMAKE_EXTENSION_SHLIB").first();
+ QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " ";
+ if(project->isActiveConfig("debug"))
+ incr_lflags += var("QMAKE_LFLAGS_DEBUG");
+ else
+ incr_lflags += var("QMAKE_LFLAGS_RELEASE");
+ t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)" << "\n\t";
+ if(!destdir.isEmpty())
+ t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t";
+ t << "$(LINK) " << incr_lflags << " -o "<< incr_target_dir <<
+ " $(INCREMENTAL_OBJECTS)" << endl;
+ //communicated below
+ if(!destdir.isEmpty()) {
+ if(!incr_objs.isEmpty())
+ incr_objs += " ";
+ incr_objs += "-L" + destdir;
+ } else {
+ if(!incr_objs.isEmpty())
+ incr_objs += " ";
+ incr_objs += "-L" + qmake_getpwd();
+ }
+ if(!incr_objs.isEmpty())
+ incr_objs += " ";
+ incr_objs += " -l" + incr_target;
+ deps.prepend(incr_target_dir + " ");
+ incr_deps = "$(OBJECTS)";
+ }
+ t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)"
+ << endl << endl;
+
+ //real target
+ t << var("TARGET") << ": " << var("PRE_TARGETDEPS") << " " << incr_deps << " " << target_deps
+ << " " << var("POST_TARGETDEPS") << "\n\t";
+ if(!destdir.isEmpty())
+ t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t";
+ if(!project->isEmpty("QMAKE_PRE_LINK"))
+ t << var("QMAKE_PRE_LINK") << "\n\t";
+ t << "$(LINK) $(LFLAGS) -o $(TARGET) " << incr_deps << " " << incr_objs << " $(OBJCOMP) $(LIBS)";
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ } else {
+ t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)"
+ << endl << endl;
+
+ t << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) "
+ << target_deps << " " << var("POST_TARGETDEPS") << "\n\t";
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ if(!project->isEmpty("QMAKE_PRE_LINK"))
+ t << var("QMAKE_PRE_LINK") << "\n\t";
+ t << "$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)";
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ }
+ } else if(!project->isActiveConfig("staticlib")) {
+ QString destdir = unescapeFilePath(project->first("DESTDIR")), incr_deps;
+ if(!project->isEmpty("QMAKE_BUNDLE")) {
+ QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION");
+ if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/"))
+ bundle_loc.prepend("/");
+ if(!bundle_loc.endsWith("/"))
+ bundle_loc += "/";
+ destdir += project->first("QMAKE_BUNDLE") + bundle_loc;
+ }
+ destdir = escapeFilePath(destdir);
+
+ if(do_incremental) {
+ QString s_ext = project->values("QMAKE_EXTENSION_SHLIB").first();
+ QString incr_target = var("QMAKE_ORIG_TARGET").replace(
+ QRegExp("\\." + s_ext), "").replace(QRegExp("^lib"), "") + "_incremental";
+ if(incr_target.indexOf(Option::dir_sep) != -1)
+ incr_target = incr_target.right(incr_target.length() -
+ (incr_target.lastIndexOf(Option::dir_sep) + 1));
+ incr_target = escapeFilePath(incr_target);
+
+ if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") {
+ QString incr_target_dir = escapeFilePath(var("OBJECTS_DIR") + incr_target + Option::obj_ext);
+ //actual target
+ const QString link_deps = "$(OBJECTS) ";
+ t << incr_target_dir << ": " << link_deps << "\n\t"
+ << "ld -r -o " << incr_target_dir << " " << link_deps << endl;
+ //communicated below
+ QStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD");
+ cmd.first().replace("$(OBJECTS) ", "$(INCREMENTAL_OBJECTS)"); //ick
+ cmd.append(incr_target_dir);
+ deps.prepend(incr_target_dir + " ");
+ incr_deps = "$(INCREMENTAL_OBJECTS)";
+ } else {
+ //actual target
+ QString incr_target_dir = escapeFilePath(destdir + "lib" + incr_target + "." + s_ext);
+ QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " ";
+ if(!project->isEmpty("QMAKE_LFLAGS_INCREMENTAL"))
+ incr_lflags += var("QMAKE_LFLAGS_INCREMENTAL") + " ";
+ if(project->isActiveConfig("debug"))
+ incr_lflags += var("QMAKE_LFLAGS_DEBUG");
+ else
+ incr_lflags += var("QMAKE_LFLAGS_RELEASE");
+ t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)" << "\n\t";
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ t << "$(LINK) " << incr_lflags << " -o "<< incr_target_dir <<
+ " $(INCREMENTAL_OBJECTS)" << endl;
+ //communicated below
+ QStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD");
+ if(!destdir.isEmpty())
+ cmd.append(" -L" + destdir);
+ cmd.append(" -l" + incr_target);
+ deps.prepend(incr_target_dir + " ");
+ incr_deps = "$(OBJECTS)";
+ }
+
+ t << "all: " << " " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ")
+ << " " << destdir << "$(TARGET)" << endl << endl;
+
+ //real target
+ t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " "
+ << incr_deps << " $(SUBLIBS) " << target_deps << " " << var("POST_TARGETDEPS");
+ } else {
+ t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << " " <<
+ destdir << "$(TARGET)" << endl << endl;
+ t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS")
+ << " $(OBJECTS) $(SUBLIBS) $(OBJCOMP) " << target_deps
+ << " " << var("POST_TARGETDEPS");
+ }
+ if(!destdir.isEmpty())
+ t << "\n\t" << mkdir_p_asstring(destdir);
+ if(!project->isEmpty("QMAKE_PRE_LINK"))
+ t << "\n\t" << var("QMAKE_PRE_LINK");
+
+ if(project->isActiveConfig("compile_libtool")) {
+ t << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD");
+ } else if(project->isActiveConfig("plugin")) {
+ t << "\n\t"
+ << "-$(DEL_FILE) $(TARGET)" << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD");
+ if(!destdir.isEmpty())
+ t << "\n\t"
+ << "-$(MOVE) $(TARGET) " << destdir;
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ } else if(!project->isEmpty("QMAKE_BUNDLE")) {
+ t << "\n\t"
+ << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(DESTDIR)$(TARGET0)" << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD") << "\n\t"
+ << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGETD)`\"", false) << "\n\t"
+ << "-$(MOVE) $(TARGET) $(DESTDIR)$(TARGETD)" << "\n\t"
+ << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGET0)`\"", false) << "\n\t"
+ << varGlue("QMAKE_LN_SHLIB","-"," "," Versions/" +
+ project->first("QMAKE_FRAMEWORK_VERSION") +
+ "/$(TARGET) $(DESTDIR)$(TARGET0)") << "\n\t"
+ << "-$(DEL_FILE) " << destdir << "Versions/Current" << "\n\t"
+ << varGlue("QMAKE_LN_SHLIB","-"," ", " " + project->first("QMAKE_FRAMEWORK_VERSION") +
+ " " + destdir + "Versions/Current") << "\n\t";
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ } else if(!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) {
+ t << "\n\t"
+ << "-$(DEL_FILE) $(TARGET)" << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD");
+ if(!destdir.isEmpty())
+ t << "\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t"
+ << "-$(MOVE) $(TARGET) " << destdir;
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ } else if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
+ t << "\n\t"
+ << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2)" << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD") << "\n\t";
+ t << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET0)") << "\n\t"
+ << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET1)") << "\n\t"
+ << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET2)");
+ if(!destdir.isEmpty())
+ t << "\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET1)\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET2)\n\t"
+ << "-$(MOVE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) " << destdir;
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ } else {
+ t << "\n\t"
+ << "-$(DEL_FILE) $(TARGET) $(TARGET0)" << "\n\t"
+ << var("QMAKE_LINK_SHLIB_CMD") << "\n\t";
+ t << varGlue("QMAKE_LN_SHLIB",""," "," $(TARGET) $(TARGET0)");
+ if(!destdir.isEmpty())
+ t << "\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t"
+ << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t"
+ << "-$(MOVE) $(TARGET) $(TARGET0) " << destdir;
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\n\t" << var("QMAKE_POST_LINK");
+ t << endl << endl;
+ }
+ t << endl << endl;
+
+ if (! project->isActiveConfig("plugin")) {
+ t << "staticlib: $(TARGETA)" << endl << endl;
+ t << "$(TARGETA): " << var("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP)";
+ if(do_incremental)
+ t << " $(INCREMENTAL_OBJECTS)";
+ t << " " << var("POST_TARGETDEPS") << "\n\t"
+ << "-$(DEL_FILE) $(TARGETA) " << "\n\t"
+ << var("QMAKE_AR_CMD");
+ if(do_incremental)
+ t << " $(INCREMENTAL_OBJECTS)";
+ if(!project->isEmpty("QMAKE_RANLIB"))
+ t << "\n\t" << "$(RANLIB) $(TARGETA)";
+ t << endl << endl;
+ }
+ } else {
+ QString destdir = project->first("DESTDIR");
+ t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << destdir << "$(TARGET) "
+ << varGlue("QMAKE_AR_SUBLIBS", destdir, " " + destdir, "") << "\n\n"
+ << "staticlib: " << destdir << "$(TARGET)" << "\n\n";
+ if(project->isEmpty("QMAKE_AR_SUBLIBS")) {
+ t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS")
+ << " $(OBJECTS) $(OBJCOMP) " << var("POST_TARGETDEPS") << "\n\t";
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ t << "-$(DEL_FILE) $(TARGET)" << "\n\t"
+ << var("QMAKE_AR_CMD") << "\n";
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\t" << var("QMAKE_POST_LINK") << "\n";
+ if(!project->isEmpty("QMAKE_RANLIB"))
+ t << "\t" << "$(RANLIB) $(TARGET)" << "\n";
+ if(!destdir.isEmpty())
+ t << "\t" << "-$(DEL_FILE) " << destdir << "$(TARGET)" << "\n"
+ << "\t" << "-$(MOVE) $(TARGET) " << destdir << "\n";
+ } else {
+ int max_files = project->first("QMAKE_MAX_FILES_PER_AR").toInt();
+ QStringList objs = project->values("OBJECTS") + project->values("OBJCOMP"),
+ libs = project->values("QMAKE_AR_SUBLIBS");
+ libs.prepend("$(TARGET)");
+ for(QStringList::Iterator libit = libs.begin(), objit = objs.begin();
+ libit != libs.end(); ++libit) {
+ QStringList build;
+ for(int cnt = 0; cnt < max_files && objit != objs.end(); ++objit, cnt++)
+ build << (*objit);
+ QString ar;
+ if((*libit) == "$(TARGET)") {
+ t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS")
+ << " " << var("POST_TARGETDEPS") << valList(build) << "\n\t";
+ ar = project->values("QMAKE_AR_CMD").first();
+ ar = ar.replace("$(OBJECTS)", build.join(" "));
+ } else {
+ t << (*libit) << ": " << valList(build) << "\n\t";
+ ar = "$(AR) " + (*libit) + " " + build.join(" ");
+ }
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ t << "-$(DEL_FILE) " << (*libit) << "\n\t"
+ << ar << "\n";
+ if(!project->isEmpty("QMAKE_POST_LINK"))
+ t << "\t" << var("QMAKE_POST_LINK") << "\n";
+ if(!project->isEmpty("QMAKE_RANLIB"))
+ t << "\t" << "$(RANLIB) " << (*libit) << "\n";
+ if(!destdir.isEmpty())
+ t << "\t" << "-$(DEL_FILE) " << destdir << (*libit) << "\n"
+ << "\t" << "-$(MOVE) " << (*libit) << " " << destdir << "\n";
+ }
+ }
+ t << endl << endl;
+ }
+
+ writeMakeQmake(t);
+ if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isActiveConfig("no_autoqmake")) {
+ QString meta_files;
+ if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib" &&
+ !project->isActiveConfig("compile_libtool")) { //libtool
+ if(!meta_files.isEmpty())
+ meta_files += " ";
+ meta_files += libtoolFileName();
+ }
+ if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") { //pkg-config
+ if(!meta_files.isEmpty())
+ meta_files += " ";
+ meta_files += pkgConfigFileName();
+ }
+ if(!meta_files.isEmpty())
+ t << escapeDependencyPath(meta_files) << ": " << "\n\t"
+ << "@$(QMAKE) -prl " << buildArgs() << " " << project->projectFile() << endl;
+ }
+
+ if(!project->first("QMAKE_PKGINFO").isEmpty()) {
+ QString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO"));
+ QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents");
+ t << pkginfo << ": " << "\n\t";
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ t << "@$(DEL_FILE) " << pkginfo << "\n\t"
+ << "@echo \"APPL"
+ << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4))
+ << "\" >" << pkginfo << endl;
+ }
+ if(!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) {
+ QString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE"));
+ QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources");
+ t << resources << ": " << "\n\t";
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ t << "@touch " << resources << "\n\t" << endl;
+ }
+ if(!project->isEmpty("QMAKE_BUNDLE")) {
+ //copy the plist
+ QString info_plist = escapeFilePath(fileFixify(project->first("QMAKE_INFO_PLIST"))),
+ info_plist_out = escapeFilePath(project->first("QMAKE_INFO_PLIST_OUT"));
+ QString destdir = info_plist_out.section(Option::dir_sep, 0, -2);
+ t << info_plist_out << ": " << "\n\t";
+ if(!destdir.isEmpty())
+ t << mkdir_p_asstring(destdir) << "\n\t";
+ QStringList commonSedArgs;
+ if (!project->values("VERSION").isEmpty())
+ commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",g\" ";
+ commonSedArgs << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ?
+ QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" ";
+ if(project->first("TEMPLATE") == "app") {
+ QString icon = fileFixify(var("ICON"));
+ t << "@$(DEL_FILE) " << info_plist_out << "\n\t"
+ << "@sed ";
+ foreach (const QString &arg, commonSedArgs)
+ t << arg;
+ t << "-e \"s,@ICON@," << icon.section(Option::dir_sep, -1) << ",g\" "
+ << "-e \"s,@EXECUTABLE@," << var("QMAKE_ORIG_TARGET") << ",g\" "
+ << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ?
+ QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "
+ << "" << info_plist << " >" << info_plist_out << endl;
+ //copy the icon
+ if(!project->isEmpty("ICON")) {
+ QString dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources/";
+ const QString icon_path = escapeFilePath(dir + icon.section(Option::dir_sep, -1));
+ t << icon_path << ": " << icon << "\n\t"
+ << mkdir_p_asstring(dir) << "\n\t"
+ << "@$(DEL_FILE) " << icon_path << "\n\t"
+ << "@$(COPY_FILE) " << escapeFilePath(icon) << " " << icon_path << endl;
+ }
+ } else {
+ t << "@$(DEL_FILE) " << info_plist_out << "\n\t"
+ << "@sed ";
+ foreach (const QString &arg, commonSedArgs)
+ t << arg;
+ t << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" "
+ << "-e \"s,@TYPEINFO@,"
+ << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ?
+ QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "
+ << "" << info_plist << " >" << info_plist_out << endl;
+ }
+ //copy other data
+ if(!project->isEmpty("QMAKE_BUNDLE_DATA")) {
+ QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/";
+ const QStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA");
+ for(int i = 0; i < bundle_data.count(); i++) {
+ const QStringList &files = project->values(bundle_data[i] + ".files");
+ QString path = bundle_dir;
+ if(!project->isEmpty(bundle_data[i] + ".version")) {
+ QString version = project->first(bundle_data[i] + ".version") + "/" +
+ project->first("QMAKE_FRAMEWORK_VERSION") + "/";
+ t << Option::fixPathToLocalOS(path + project->first(bundle_data[i] + ".path")) << ": " << "\n\t"
+ << mkdir_p_asstring(path) << "\n\t"
+ << "@$(SYMLINK) " << version << project->first(bundle_data[i] + ".path") << " " << path << endl;
+ path += version;
+ }
+ path += project->first(bundle_data[i] + ".path");
+ path = Option::fixPathToLocalOS(path);
+ for(int file = 0; file < files.count(); file++) {
+ QString src = fileFixify(files[file], FileFixifyAbsolute);
+ if (!QFile::exists(src))
+ src = files[file];
+ src = escapeFilePath(src);
+ const QString dst = escapeFilePath(path + Option::dir_sep + fileInfo(files[file]).fileName());
+ t << dst << ": " << src << "\n\t"
+ << mkdir_p_asstring(path) << "\n\t";
+ QFileInfo fi(fileInfo(files[file]));
+ if(fi.isDir())
+ t << "@$(DEL_FILE) -r " << dst << "\n\t"
+ << "@$(COPY_DIR) " << src << " " << dst << endl;
+ else
+ t << "@$(DEL_FILE) " << dst << "\n\t"
+ << "@$(COPY_FILE) " << src << " " << dst << endl;
+ }
+ }
+ }
+ }
+
+ QString ddir;
+ QString packageName(project->first("QMAKE_ORIG_TARGET"));
+ if(!project->isActiveConfig("no_dist_version"))
+ packageName += var("VERSION");
+ if (project->isEmpty("QMAKE_DISTDIR"))
+ ddir = packageName;
+ else
+ ddir = project->first("QMAKE_DISTDIR");
+
+ QString ddir_c = escapeFilePath(fileFixify((project->isEmpty("OBJECTS_DIR") ? QString(".tmp/") :
+ project->first("OBJECTS_DIR")) + ddir,
+ Option::output_dir, Option::output_dir));
+ t << "dist: " << "\n\t"
+ << mkdir_p_asstring(ddir_c) << "\n\t"
+ << "$(COPY_FILE) --parents $(SOURCES) $(DIST) " << ddir_c << Option::dir_sep << " && ";
+ if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
+ const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
+ for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
+ const QStringList &var = project->values((*it)+".input");
+ for(QStringList::ConstIterator var_it = var.begin(); var_it != var.end(); ++var_it) {
+ const QStringList &val = project->values((*var_it));
+ if(val.isEmpty())
+ continue;
+ t << "$(COPY_FILE) --parents " << val.join(" ") << " " << ddir_c << Option::dir_sep << " && ";
+ }
+ }
+ }
+ if(!project->isEmpty("TRANSLATIONS"))
+ t << "$(COPY_FILE) --parents " << var("TRANSLATIONS") << " " << ddir_c << Option::dir_sep << " && ";
+ t << "(cd `dirname " << ddir_c << "` && "
+ << "$(TAR) " << packageName << ".tar " << ddir << " && "
+ << "$(COMPRESS) " << packageName << ".tar) && "
+ << "$(MOVE) `dirname " << ddir_c << "`" << Option::dir_sep << packageName << ".tar.gz . && "
+ << "$(DEL_FILE) -r " << ddir_c
+ << endl << endl;
+
+ t << endl;
+
+ QString clean_targets = "compiler_clean " + var("CLEAN_DEPS");
+ if(do_incremental) {
+ t << "incrclean:" << "\n";
+ if(src_incremental)
+ t << "\t-$(DEL_FILE) $(INCREMENTAL_OBJECTS)" << "\n";
+ t << endl;
+ }
+
+ t << "clean:" << clean_targets << "\n\t";
+ if(!project->isEmpty("OBJECTS")) {
+ if(project->isActiveConfig("compile_libtool"))
+ t << "-$(LIBTOOL) --mode=clean $(DEL_FILE) $(OBJECTS)" << "\n\t";
+ else
+ t << "-$(DEL_FILE) $(OBJECTS)" << "\n\t";
+ }
+ if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
+ QStringList precomp_files;
+ QString precomph_out_dir;
+
+ if(!project->isEmpty("PRECOMPILED_DIR"))
+ precomph_out_dir = project->first("PRECOMPILED_DIR");
+ precomph_out_dir += project->first("QMAKE_ORIG_TARGET");
+ if (!project->isActiveConfig("clang_pch_style"))
+ precomph_out_dir += project->first("QMAKE_PCH_OUTPUT_EXT");
+
+ if (project->isActiveConfig("icc_pch_style")) {
+ // icc style
+ QString pchBaseName = project->first("QMAKE_ORIG_TARGET");
+ QString pchOutput;
+ if(!project->isEmpty("PRECOMPILED_DIR"))
+ pchOutput = project->first("PRECOMPILED_DIR");
+ pchOutput += pchBaseName + project->firs