diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /qmake/generators | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'qmake/generators')
29 files changed, 19548 insertions, 0 deletions
diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp new file mode 100644 index 0000000..473a625 --- /dev/null +++ b/qmake/generators/mac/pbuilder_pbx.cpp @@ -0,0 +1,1860 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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() + ".pro"; + 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) { + 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 = " << (project->isEmpty("QMAKE_QMAKE") ? + QString((QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/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")); + 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 0000000..c9f5ae9 --- /dev/null +++ b/qmake/generators/mac/pbuilder_pbx.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..24e030e --- /dev/null +++ b/qmake/generators/makefile.cpp @@ -0,0 +1,3023 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 = path.right(path.length() - 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 = path.right(path.length() - 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 = Option::fixPathToTargetOS(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 = Option::fixPathToTargetOS(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 = Option::fixPathToTargetOS(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.right(Option::dir_sep.length()) != 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); + 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"); + } + QDir::current().cd(currentDir); +} + +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.right(Option::dir_sep.length()) != 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 = regex.right(regex.length() - 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 = dir + files[i]; + if(!(flags & VPATH_NoFixify)) + a = fileFixify(a); + l.insert(val_it, a); + } + } + } else { + debug_msg(1, "%s:%d Cannot match %s%c%s, as %s does not exist.", + __FILE__, __LINE__, real_dir.toLatin1().constData(), + QDir::separator().toLatin1(), + 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) { + if(!subs.at(i).endsWith(".in")) { + warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'", + subs.at(i).toLatin1().constData()); + continue; + } + QFile in(fileFixify(subs.at(i))), out(fileInfo(subs.at(i)).fileName()); + if(out.fileName().endsWith(".in")) + out.setFileName(out.fileName().left(out.fileName().length()-3)); + 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).join(QString(Option::field_sep)); + } + } + if(out.exists() && out.open(QFile::ReadOnly)) { + QString old = QString::fromUtf8(out.readAll()); + if(contents == old) { + v["QMAKE_INTERNAL_INCLUDED_FILES"].append(subs.at(i)); + continue; + } + out.close(); + if(!out.remove()) { + warn_msg(WarnLogic, "Cannot clear substitute '%s'", + out.fileName().toLatin1().constData()); + continue; + } + } + if(out.open(QFile::WriteOnly)) { + v["QMAKE_INTERNAL_INCLUDED_FILES"].append(subs.at(i)); + 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 = Option::fixPathToLocalOS(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(QDir::separator()) == -1) + cache_file.prepend(Option::output_dir + QDir::separator()); + 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(fileFixify(Option::output_dir) != fileFixify(qmake_getpwd())) + project->values("INCLUDEPATH").append(fileFixify(Option::output_dir, + Option::output_dir, + Option::output_dir)); + } + + //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 = regex.right(regex.length() - 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 + if (!project->isEmpty("QMAKE_QMAKE")) { + project->values("QMAKE_QMAKE") = escapeFilePaths(project->values("QMAKE_QMAKE")); + } +} + +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 = target.right(target.length() - 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 + t << "QMAKE_PRL_LIBS = "; + for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) + t << project->values((*it)).join(" ") << " "; + 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 = ret.right(ret.length() - slsh); + if(!ret.endsWith(Option::prl_ext)) { + int dot = ret.indexOf('.'); + if(dot != -1) + ret = ret.left(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.right(1) != 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::fixPathToLocalOS((*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 = filestr.right(filestr.length() - slsh - 1); + } + if(dirstr.right(Option::dir_sep.length()) != Option::dir_sep) + dirstr += Option::dir_sep; + if(exists(wild)) { //real file + 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 (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)); + if(project->values((*it) + ".CONFIG").indexOf("no_check_exist") != -1 && files.isEmpty()) { + if(!target.isEmpty()) + target += "\t"; + QString dst_file = filePrefixRoot(root, dst); + QFileInfo fi(fileInfo(wild)); + QString cmd = QString(fi.isExecutable() ? "-$(INSTALL_PROGRAM)" : "-$(INSTALL_FILE)") + " " + + 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.right(1) != 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) +{ + hash = 0; + pwd = qmake_getpwd(); + var = v; + { + QStringList il = i; + il.sort(); + in = il.join("::"); + } + { + QStringList ol = o; + ol.sort(); + out = ol.join("::"); + } +} + +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(12); + 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_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 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); + 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; + } +} + +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; + 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)); + tmp_dep_cmd = cmdline.join(" "); + } else { + cmdline[argv0] = escapeFilePath(cmdline.at(argv0)); + } + } + } + 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 = 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 = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out)); + t << 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))); + } else { + t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps)); + } + t << "\n\t" << cmd << endl << endl; + 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); + 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 = 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; + } + } + 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 = " << (project->isEmpty("QMAKE_QMAKE") ? QString("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 == Option::TARG_MAC9_MODE) + ret += " -mac9"; + else 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"; + else if(Option::target_mode == Option::TARG_QNX6_MODE) + ret += " -qnx6"; + + //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)", + (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : var("QMAKE_QMAKE"))) << endl; + t << "#############################################################################" << endl; + t << endl; +} + +void +MakefileGenerator::writeSubDirs(QTextStream &t) +{ + 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.right(1) == Option::dir_sep) + st->in_directory = st->in_directory.left(st->in_directory.length() - 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_]"),"-"); + } + } + } + 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; + + QString ofile = Option::fixPathToTargetOS(Option::output.fileName()); + if(ofile.lastIndexOf(Option::dir_sep) != -1) + ofile = ofile.right(ofile.length() - 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 = " << Option::fixPathToTargetOS(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; + writeExtraVariables(t); + 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; + + QStringList targetSuffixes; + const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH"); + 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 = out_directory + subtarget->makefile, + in = fileFixify(in_directory + subtarget->profile, in_directory); + if(in.startsWith(in_directory)) + in = in.mid(in_directory.length()); + if(out.startsWith(in_directory)) + out = out.mid(in_directory.length()); + t << mkfile << ": " << "\n\t"; + if(!in_directory.isEmpty()) { + t << mkdir_p_asstring(in_directory) + << in_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(in_directory) + << in_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(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) { + QString targetRule = targets.at(target)->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; + } 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") << " "; + else if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"tmake.conf"))) + t << escapeDependencyPath(specdir() + Option::dir_sep + "tmake.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_QHashFileInfoCacheKeyQFileInfo, (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); + + //setup the cache + static QHash<FileFixifyCacheKey, QString> *cache = 0; + if(!cache) { + cache = new QHash<FileFixifyCacheKey, QString>; + qmakeAddCacheClear(qmakeDeleteCacheClear_QHashFileFixifyCacheKeyQString, (void**)&cache); + } + FileFixifyCacheKey cacheKey(ret, out_d, in_d, fix, canon); + QString cacheVal = cache->value(cacheKey); + if(!cacheVal.isNull()) + return cacheVal; + + //do the fixin' + const QString pwd = qmake_getpwd() + "/"; + QString orig_file = ret; + if(ret.startsWith(QLatin1Char('~'))) { + if(ret.startsWith(QLatin1String("~/"))) + ret = QDir::homePath() + Option::dir_sep + 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 + ret.prepend(pwd); + ret = Option::fixPathToTargetOS(ret, false, canon); + } else { //fix it.. + QString qfile(Option::fixPathToLocalOS(ret, true, canon)), in_dir(in_d), out_dir(out_d); + { + if(out_dir.isNull() || QDir::isRelativePath(out_dir)) + out_dir.prepend(Option::output_dir + "/"); + else if(out_dir == ".") + out_dir = pwd; + if(in_dir.isEmpty() || QDir::isRelativePath(in_dir)) + in_dir.prepend(pwd); + else if(in_dir == ".") + in_dir = pwd; + 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(); + } + + 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; + } + } + ret.prepend(dot_prefix); + } + } else { + ret = Option::fixPathToTargetOS(ret, false, canon); + } + } + if(ret.isEmpty()) + ret = "."; + debug_msg(3, "Fixed[%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, orig_file.toLatin1().constData(), + ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(), + pwd.toLatin1().constData(), Option::output_dir.toLatin1().constData()); + cache->insert(cacheKey, ret); + 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 = file.right(file.length() - 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 = file2.right(file2.length() - 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() + QDir::separator(); + } + 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(Option::dir_sep); + 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 = Option::fixPathToTargetOS(od); + if(QDir::isRelativePath(od)) + od.prepend(Option::output_dir); + Option::output_dir = od; + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h new file mode 100644 index 0000000..a26a247 --- /dev/null +++ b/qmake/generators/makefile.h @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + + //generating subtarget makefiles + struct SubTarget + { + QString name; + QString in_directory, out_directory; + QString profile, target, makefile; + QStringList depends; + }; + enum SubTargetFlags { + SubTargetInstalls=0x01, + SubTargetOrdered=0x02, + + SubTargetsNoFlags=0x00 + }; + 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::target_mode == Option::TARG_WIN_MODE; } +}; + +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 0000000..852d6a2 --- /dev/null +++ b/qmake/generators/makefiledeps.cpp @@ -0,0 +1,963 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + + struct stat fst; + char *buffer = 0; + int buffer_len = 0; + { + int fd; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLatin1().constData(), + _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0) + fd = -1; +#else + fd = open(fixPathForFile(file->file, true).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) { + QString dir = findFileInfo(file->file).path(); + if(QDir::isRelativePath(dir)) + dir.prepend(qmake_getpwd() + "/"); + if(!dir.endsWith("/")) + dir += "/"; + QMakeLocalFileName f(dir + 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 0000000..182d401 --- /dev/null +++ b/qmake/generators/makefiledeps.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..f0683a7 --- /dev/null +++ b/qmake/generators/metamakefile.cpp @@ -0,0 +1,490 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; +} + +class BuildsMetaMakefileGenerator : public MetaMakefileGenerator +{ + bool init_flag; +private: + 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); + makefiles += build; + } + 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) { + 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 +{ + bool init_flag; +private: + 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; + + if(Option::recursive) { + QString old_output_dir = QDir::cleanPath(Option::output_dir); + if(!old_output_dir.endsWith('/')) + old_output_dir += '/'; + QString old_output = Option::output.fileName(); + QString oldpwd = QDir::cleanPath(qmake_getpwd()); + if(!oldpwd.endsWith('/')) + oldpwd += '/'; + 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(oldpwd)) + subdir = QFileInfo(subdir_path.mid(oldpwd.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; + if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/')) + Option::output_dir += QLatin1Char('/'); + 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; + continue; + } + 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); + 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(!Option::recursive || (!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 true; +} + +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_dsp.h" +#include "msvc_vcproj.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, "No generator specified in config file: %s\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") { + // Visual Studio =< v6.0 + if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1) + mkfile = new DspMakefileGenerator; + else + mkfile = new NmakeMakefileGenerator; + } else if(gen == "MSVC.NET") { + // Visual Studio >= v7.0 + if(proj->first("TEMPLATE").indexOf(QRegExp("^vc.*")) != -1 || proj->first("TEMPLATE").indexOf(QRegExp("^ce.*")) != -1) + mkfile = new VcprojGenerator; + else + mkfile = new NmakeMakefileGenerator; + } else if(gen == "BMAKE") { + mkfile = new BorlandMakefileGenerator; + } 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) +{ + 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); + ret->init(); + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/metamakefile.h b/qmake/generators/metamakefile.h new file mode 100644 index 0000000..cba0c75 --- /dev/null +++ b/qmake/generators/metamakefile.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef METAMAKEFILE_H +#define METAMAKEFILE_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); + static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false); + + 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 0000000..7215818 --- /dev/null +++ b/qmake/generators/projectgenerator.cpp @@ -0,0 +1,510 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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) { + 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) { + 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) { + 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 && !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 0000000..8402d32 --- /dev/null +++ b/qmake/generators/projectgenerator.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp new file mode 100644 index 0000000..1f02fb5 --- /dev/null +++ b/qmake/generators/unix/unixmake.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#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_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 -sf"); + + /* 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->isEmpty("QMAKE_QMAKE")) + project->values("QMAKE_QMAKE").append("qmake"); + 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")); + 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 { + 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")); + } + + 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") + 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 + 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"; + 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"; + if(!ret.contains(precomp_objc_h)) + ret += precomp_objc_h; + } + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) { + QString precomp_objcpp_h = header_prefix + "objective-c++"; + 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++"; + 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++"; + 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", 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->isActiveConfig("rvct_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 + extens << project->values("QMAKE_EXTENSION_SHLIB").first() << "a"; + for(QStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) { + if(dir.isNull()) { + QString lib_stub; + for(QList<QMakeLocalFileName>::Iterator dep_it = libdirs.begin(); dep_it != libdirs.end(); ++dep_it) { + if(exists((*dep_it).local() + Option::dir_sep + "lib" + stub + + "." + (*extit))) { + lib_stub = stub; + break; + } + } + if(!lib_stub.isNull()) { + (*it) = "-l" + lib_stub; + found = true; + break; + } + } else { + if(exists("lib" + stub + "." + (*extit))) { + (*it) = "lib" + 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 + "lib" + stub + Option::libtool_ext)) { + (*it) = libdirs[dep_i].real() + Option::dir_sep + "lib" + 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")); + frameworkdirs.append(QMakeLocalFileName("/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 + "lib" + 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 + "lib" + 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")) { + QStringList lflags; + for(int lit = 0; lit < l.size(); ++lit) { + QString opt = l.at(lit).trimmed(); + if(opt.startsWith("-")) { + if(opt.startsWith("-L") || + (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F"))) { + if(lit == 0 || l.lastIndexOf(opt, lit-1) == -1) + lflags.append(opt); + } else if(opt.startsWith("-l")) { + if(lit == l.size()-1 || l.indexOf(opt, lit+1) == -1) + lflags.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); + bool found = false; + for(int x = lit+1; x < l.size(); ++x) { + QString xf = l.at(x); + if(xf.startsWith("-framework")) { + QString framework; + if(xf.length() > 11) + framework = xf.mid(11); + else + framework = l.at(++x); + if(framework == opt) { + found = true; + break; + } + } + } + if(!found) { + lflags.append("-framework"); + lflags.append(opt); + } + } else { + lflags.append(opt); + } + } else if(!opt.isNull()) { + if(lit == 0 || l.lastIndexOf(opt, lit-1) == -1) + lflags.append(opt); + } + } + l = lflags; + } + } +} + +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-" + var("QMAKE_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_WIN_MODE || + Option::target_mode == Option::TARG_MAC9_MODE) { + } else 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 0000000..a1c6233 --- /dev/null +++ b/qmake/generators/unix/unixmake.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + bool writeMakefile(QTextStream &); + QString libtoolFileName(bool fixify=true); + void writeLibtoolFile(); // for libtool + QString pkgConfigPrefix() const; + QString pkgConfigFileName(bool fixify=true); + QString pkgConfigFixPath(QString) const; + void writePkgConfigFile(); // for pkg-config + 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 &); + +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 0000000..585ab55 --- /dev/null +++ b/qmake/generators/unix/unixmake2.cpp @@ -0,0 +1,1477 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 = " << (project->isEmpty("QMAKE_QMAKE") ? QString("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_FRAMEWORKDIR_FLAGS") << " " + << var("QMAKE_LIBDIR_FLAGS") << " " << var("QMAKE_LIBS") << endl; + } + + t << "AR = " << var("QMAKE_AR") << endl; + t << "RANLIB = " << var("QMAKE_RANLIB") << endl; + t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("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 << "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; + 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_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 << "lib" << (*it) << ".a "; + 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") << "\n\t"; + 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_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"; + if(project->first("TEMPLATE") == "app") { + QString icon = fileFixify(var("ICON")); + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed " + << "-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 " + << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",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)); + 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") + 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->first("QMAKE_PCH_OUTPUT_EXT"); + QString sourceFile = pchOutput + Option::cpp_ext.first(); + QString objectFile = createObjectList(QStringList(sourceFile)).first(); + + precomp_files << precomph_out_dir << sourceFile << objectFile; + } else { + // gcc style + precomph_out_dir += Option::dir_sep; + + QString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + if(!project->isEmpty("QMAKE_CFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c"; + if(!project->isEmpty("QMAKE_CXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c++"; + if(project->isActiveConfig("objective_c")) { + if(!project->isEmpty("QMAKE_OBJCFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c"; + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c++"; + } + } + t << "-$(DEL_FILE) " << precomp_files.join(" ") << "\n\t"; + } + if(!project->isEmpty("IMAGES")) + t << varGlue("QMAKE_IMAGE_COLLECTION", "\t-$(DEL_FILE) ", " ", "") << "\n\t"; + if(src_incremental) + t << "-$(DEL_FILE) $(INCREMENTAL_OBJECTS)" << "\n\t"; + t << varGlue("QMAKE_CLEAN","-$(DEL_FILE) "," ","\n\t") + << "-$(DEL_FILE) *~ core *.core" << "\n" + << varGlue("CLEAN_FILES","\t-$(DEL_FILE) "," ","") << endl << endl; + t << "####### Sub-libraries" << endl << endl; + if (!project->values("SUBLIBS").isEmpty()) { + QString libdir = "tmp/"; + if(!project->isEmpty("SUBLIBS_DIR")) + libdir = project->first("SUBLIBS_DIR"); + QStringList &l = project->values("SUBLIBS"); + for(it = l.begin(); it != l.end(); ++it) + t << libdir << "lib" << (*it) << ".a" << ":\n\t" + << var(QString("MAKELIB") + (*it)) << endl << endl; + } + + QString destdir = project->first("DESTDIR"); + if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep) + destdir += Option::dir_sep; + t << "distclean: " << "clean\n"; + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundlePath = escapeFilePath(destdir + project->first("QMAKE_BUNDLE")); + t << "\t-$(DEL_FILE) -r " << bundlePath << endl; + } else if(project->isActiveConfig("compile_libtool")) { + t << "\t-$(LIBTOOL) --mode=clean $(DEL_FILE) " << "$(TARGET)" << endl; + } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty() && + !project->isActiveConfig("plugin")) { + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET)" << " " << endl + << "\t-$(DEL_FILE) " << destdir << "$(TARGET0) " << destdir << "$(TARGET1) " + << destdir << "$(TARGET2) $(TARGETA)" << endl; + } else { + t << "\t-$(DEL_FILE) " << "$(TARGET)" << " " << endl; + } + t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n"); + { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + } + t << endl << endl; + + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + QString pchInput = project->first("PRECOMPILED_HEADER"); + t << "###### Prefix headers" << endl; + QString comps[] = { "C", "CXX", "OBJC", "OBJCXX", QString() }; + for(int i = 0; !comps[i].isNull(); i++) { + QString pchFlags = var("QMAKE_" + comps[i] + "FLAGS_PRECOMPILE"); + if(pchFlags.isEmpty()) + continue; + + QString cflags; + if(comps[i] == "OBJC" || comps[i] == "OBJCXX") + cflags += " $(CFLAGS)"; + else + cflags += " $(" + comps[i] + "FLAGS)"; + + QString pchBaseName = project->first("QMAKE_ORIG_TARGET"); + QString pchOutput; + if(!project->isEmpty("PRECOMPILED_DIR")) + pchOutput = project->first("PRECOMPILED_DIR"); + pchOutput += pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT"); + + if (project->isActiveConfig("icc_pch_style")) { + // icc style + QString sourceFile = pchOutput + Option::cpp_ext.first(); + QString objectFile = createObjectList(QStringList(sourceFile)).first(); + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\techo \"// Automatically generated, do not modify\" > " << sourceFile + << "\n\trm -f " << pchOutput; + + pchFlags = pchFlags.replace("${QMAKE_PCH_TEMP_SOURCE}", sourceFile) + .replace("${QMAKE_PCH_TEMP_OBJECT}", objectFile); + } else { + // gcc style + QString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + + pchOutput += Option::dir_sep; + QString pchOutputDir = pchOutput, 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()) + continue; + pchOutput += header_prefix + pchOutputFile; + + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(pchOutputDir); + } + pchFlags = pchFlags.replace("${QMAKE_PCH_INPUT}", pchInput) + .replace("${QMAKE_PCH_OUTPUT_BASE}", pchBaseName) + .replace("${QMAKE_PCH_OUTPUT}", pchOutput); + + QString compiler; + if(comps[i] == "C" || comps[i] == "OBJC" || comps[i] == "OBJCXX") + compiler = "$(CC)"; + else + compiler = "$(CXX)"; + + // compile command + t << "\n\t" << compiler << cflags << " $(INCPATH) " << pchFlags << endl << endl; + } + } + + writeExtraTargets(t); + writeExtraCompilerTargets(t); +} + +void UnixMakefileGenerator::init2() +{ + //version handling + if(project->isEmpty("VERSION")) + project->values("VERSION").append("1.0." + + (project->isEmpty("VER_PAT") ? QString("0") : + project->first("VER_PAT"))); + QStringList l = project->first("VERSION").split('.'); + l << "0" << "0"; //make sure there are three + project->values("VER_MAJ").append(l[0]); + project->values("VER_MIN").append(l[1]); + project->values("VER_PAT").append(l[2]); + if(project->isEmpty("QMAKE_FRAMEWORK_VERSION")) + project->values("QMAKE_FRAMEWORK_VERSION").append(project->values("VER_MAJ").first()); + + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + 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 += "/"; + project->values("TARGET").first().prepend(project->first("QMAKE_BUNDLE") + bundle_loc); + } + if(!project->isEmpty("TARGET")) + project->values("TARGET").first().prepend(project->first("DESTDIR")); + if (!project->values("QMAKE_CYGWIN_EXE").isEmpty()) + project->values("TARGET_EXT").append(".exe"); + } else if (project->isActiveConfig("staticlib")) { + project->values("TARGET").first().prepend("lib"); + project->values("TARGET").first() += ".a"; + if(project->values("QMAKE_AR_CMD").isEmpty()) + project->values("QMAKE_AR_CMD").append("$(AR) $(TARGET) $(OBJECTS)"); + } else { + project->values("TARGETA").append(project->first("DESTDIR") + "lib" + project->first("TARGET") + ".a"); + if(project->isActiveConfig("compile_libtool")) + project->values("TARGET_la") = QStringList(project->first("DESTDIR") + "lib" + project->first("TARGET") + Option::libtool_ext); + + if (!project->values("QMAKE_AR_CMD").isEmpty()) + project->values("QMAKE_AR_CMD").first().replace("(TARGET)","(TARGETA)"); + else + project->values("QMAKE_AR_CMD").append("$(AR) $(TARGETA) $(OBJECTS)"); + if(project->isActiveConfig("compile_libtool")) { + project->values("TARGET") = project->values("TARGET_la"); + } else 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 += "/"; + project->values("TARGET_").append(project->first("QMAKE_BUNDLE") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + project->values("TARGET_x.y").append(project->first("QMAKE_BUNDLE") + + "/Versions/" + + project->first("QMAKE_FRAMEWORK_VERSION") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + } else if(project->isActiveConfig("plugin")) { + QString prefix; + if(!project->isActiveConfig("no_plugin_name_prefix")) + prefix = "lib"; + project->values("TARGET_x.y.z").append(prefix + + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + else + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN") + + "." + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else if (!project->isEmpty("QMAKE_HPUX_SHLIB")) { + project->values("TARGET_").append("lib" + project->first("TARGET") + ".sl"); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append("lib" + project->first("VER_MAJ") + "." + + project->first("TARGET")); + else + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x"); + } else if (!project->isEmpty("QMAKE_AIX_SHLIB")) { + project->values("TARGET_").append("lib" + project->first("TARGET") + ".a"); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else { + project->values("TARGET_").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->values("QMAKE_EXTENSION_SHLIB").first()); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + + "." + + project->values( + "QMAKE_EXTENSION_SHLIB").first() + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + project->values("TARGET") = project->values("TARGET_x.y.z"); + } + if(project->isEmpty("QMAKE_LN_SHLIB")) + project->values("QMAKE_LN_SHLIB").append("ln -s"); + if (!project->values("QMAKE_LFLAGS_SONAME").isEmpty()) { + QString soname; + if(project->isActiveConfig("plugin")) { + if(!project->values("TARGET").isEmpty()) + soname += project->first("TARGET"); + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + soname += project->first("TARGET_x.y"); + } else if(!project->values("TARGET_x").isEmpty()) { + soname += project->first("TARGET_x"); + } + if(!soname.isEmpty()) { + if(project->isActiveConfig("absolute_library_soname") && + project->values("INSTALLS").indexOf("target") != -1 && + !project->isEmpty("target.path")) { + QString instpath = Option::fixPathToTargetOS(project->first("target.path")); + if(!instpath.endsWith(Option::dir_sep)) + instpath += Option::dir_sep; + soname.prepend(instpath); + } + project->values("QMAKE_LFLAGS_SONAME").first() += escapeFilePath(soname); + } + } + if (project->values("QMAKE_LINK_SHLIB_CMD").isEmpty()) + project->values("QMAKE_LINK_SHLIB_CMD").append( + "$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP)"); + } + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_APP"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_APP"); + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_APP"); + } else if (project->isActiveConfig("dll")) { + if(!project->isActiveConfig("plugin") || !project->isActiveConfig("plugin_no_share_shlib_cflags")) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_SHLIB"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_SHLIB"); + } + if (project->isActiveConfig("plugin")) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_PLUGIN"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_PLUGIN"); + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PLUGIN"); + if(project->isActiveConfig("plugin_with_soname") && !project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } else { + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SHLIB"); + if(!project->isEmpty("QMAKE_LFLAGS_COMPAT_VERSION")) { + if(project->isEmpty("COMPAT_VERSION")) + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN")); + else + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("COMPATIBILITY_VERSION")); + } + if(!project->isEmpty("QMAKE_LFLAGS_VERSION")) { + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + if(!project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } + } + + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString plist = fileFixify(project->first("QMAKE_INFO_PLIST")); + if(plist.isEmpty()) + plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + if(exists(Option::fixPathToLocalOS(plist))) { + if(project->isEmpty("QMAKE_INFO_PLIST")) + project->values("QMAKE_INFO_PLIST").append(plist); + project->values("QMAKE_INFO_PLIST_OUT").append(project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Info.plist"); + project->values("ALL_DEPS") += project->first("QMAKE_INFO_PLIST_OUT"); + if(!project->isEmpty("ICON") && project->first("TEMPLATE") == "app") + project->values("ALL_DEPS") += project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Resources/" + project->first("ICON").section('/', -1); + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + QStringList &alldeps = project->values("ALL_DEPS"); + 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")) { + alldeps += Option::fixPathToLocalOS(path + Option::dir_sep + + project->first(bundle_data[i] + ".path")); + path += project->first(bundle_data[i] + ".version") + "/" + + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; + } + path += project->first(bundle_data[i] + ".path"); + path = Option::fixPathToLocalOS(path); + for(int file = 0; file < files.count(); file++) + alldeps += path + Option::dir_sep + fileInfo(files[file]).fileName(); + } + } + } + } +} + +QString +UnixMakefileGenerator::libtoolFileName(bool fixify) +{ + QString ret = var("TARGET"); + int slsh = ret.lastIndexOf(Option::dir_sep); + if(slsh != -1) + ret = ret.right(ret.length() - slsh - 1); + int dot = ret.indexOf('.'); + if(dot != -1) + ret = ret.left(dot); + ret += Option::libtool_ext; + if(!project->isEmpty("QMAKE_LIBTOOL_DESTDIR")) + ret.prepend(project->first("QMAKE_LIBTOOL_DESTDIR") + Option::dir_sep); + 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; +} + +void +UnixMakefileGenerator::writeLibtoolFile() +{ + QString fname = libtoolFileName(), 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); + t << "# " << lname << " - a libtool library file\n"; + t << "# Generated by qmake/libtool (" << qmake_version() << ") (Qt " + << QT_VERSION_STR << ") on: " << QDateTime::currentDateTime().toString(); + t << "\n"; + + t << "# The name that we can dlopen(3).\n" + << "dlname='" << var(project->isActiveConfig("plugin") ? "TARGET" : "TARGET_x") + << "'\n\n"; + + t << "# Names of this library.\n"; + t << "library_names='"; + if(project->isActiveConfig("plugin")) { + t << var("TARGET"); + } else { + if (project->isEmpty("QMAKE_HPUX_SHLIB")) + t << var("TARGET_x.y.z") << " "; + t << var("TARGET_x") << " " << var("TARGET_"); + } + t << "'\n\n"; + + t << "# The name of the static archive.\n" + << "old_library='" << lname.left(lname.length()-Option::libtool_ext.length()) << ".a'\n\n"; + + t << "# Libraries that this one depends upon.\n"; + QStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + else + libs << "QMAKE_LIBS"; //obvious one + t << "dependency_libs='"; + for(QStringList::ConstIterator it = libs.begin(); it != libs.end(); ++it) + t << project->values((*it)).join(" ") << " "; + t << "'\n\n"; + + t << "# Version information for " << lname << "\n"; + int maj = project->first("VER_MAJ").toInt(); + int min = project->first("VER_MIN").toInt(); + int pat = project->first("VER_PAT").toInt(); + t << "current=" << (10*maj + min) << "\n" // best I can think of + << "age=0\n" + << "revision=" << pat << "\n\n"; + + t << "# Is this an already installed library.\n" + "installed=yes\n\n"; // ### + + t << "# Files to dlopen/dlpreopen.\n" + "dlopen=''\n" + "dlpreopen=''\n\n"; + + QString install_dir = project->first("QMAKE_LIBTOOL_LIBDIR"); + if(install_dir.isEmpty()) + install_dir = project->first("target.path"); + if(install_dir.isEmpty()) + install_dir = project->first("DESTDIR"); + t << "# Directory that this library needs to be installed in:\n" + "libdir='" << Option::fixPathToTargetOS(install_dir, false) << "'\n"; +} + +QString +UnixMakefileGenerator::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; + if(!project->isEmpty("QMAKE_PKGCONFIG_DESTDIR")) + ret.prepend(project->first("QMAKE_PKGCONFIG_DESTDIR") + Option::dir_sep); + 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 +UnixMakefileGenerator::pkgConfigPrefix() const +{ + if(!project->isEmpty("QMAKE_PKGCONFIG_PREFIX")) + return project->first("QMAKE_PKGCONFIG_PREFIX"); + return QLibraryInfo::location(QLibraryInfo::PrefixPath); +} + +QString +UnixMakefileGenerator::pkgConfigFixPath(QString path) const +{ + QString prefix = pkgConfigPrefix(); + if(path.startsWith(prefix)) + path = path.replace(prefix, "${prefix}"); + return path; +} + +void +UnixMakefileGenerator::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 + QStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) { + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + } else { + libs << "QMAKE_LIBS"; //obvious one + } + libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread? + 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; + 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/win32/borland_bmake.cpp b/qmake/generators/win32/borland_bmake.cpp new file mode 100644 index 0000000..411c7ab --- /dev/null +++ b/qmake/generators/win32/borland_bmake.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "borland_bmake.h" +#include "option.h" +#include <qdir.h> +#include <qregexp.h> +#include <time.h> + +QT_BEGIN_NAMESPACE + +BorlandMakefileGenerator::BorlandMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + +} + +bool +BorlandMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "all first clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { + writeBorlandParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; +} + +void +BorlandMakefileGenerator::writeBorlandParts(QTextStream &t) +{ + t << "!if !$d(BCB)" << endl; + t << "BCB = $(MAKEDIR)\\.." << endl; + t << "!endif" << endl << endl; + + writeStandardParts(t); +} + +void +BorlandMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + if(project->values("MAKEFILE").isEmpty()) + project->values("MAKEFILE").append("Makefile"); + if(project->values("QMAKE_QMAKE").isEmpty()) + project->values("QMAKE_QMAKE").append("qmake"); + return; + } + + processVars(); + + project->values("QMAKE_LIBS") += project->values("LIBS"); + + MakefileGenerator::init(); + + if (project->isActiveConfig("dll") || !project->values("QMAKE_APP_FLAG").isEmpty()) { + // bcc does not generate a .tds file for static libs + QString tdsPostfix; + if (!project->values("VERSION").isEmpty()) + tdsPostfix = project->first("TARGET_VERSION_EXT"); + tdsPostfix += ".tds"; + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + tdsPostfix); + } +} + +void BorlandMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << fileFixify(Option::output.fileName()) << " " << varGlue("ALL_DEPS"," "," "," ") << " $(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <<var("QMAKE_PRE_LINK"); + if(project->isActiveConfig("staticlib")) { + t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" + << "\n\t" << "$(LIB) $(DESTDIR_TARGET) @&&|" << " \n+" + << project->values("OBJECTS").join(" \\\n+") << " \\\n+" + << project->values("OBJMOC").join(" \\\n+"); + } else { + t << "\n\t" << "$(LINK) @&&|" << "\n\t" + << "$(LFLAGS) $(OBJECTS) $(OBJMOC),$(DESTDIR_TARGET),,$(LIBS),$(DEF_FILE),$(RES_FILE)"; + } + t << endl << "|"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" <<var("QMAKE_POST_LINK"); + t << endl; +} + +void BorlandMakefileGenerator::writeCleanParts(QTextStream &t) +{ + t << "clean: " + << varGlue("OBJECTS","\n\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ","") + << varGlue("QMAKE_CLEAN","\n\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ","\n") + << varGlue("CLEAN_FILES","\n\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ","\n"); + + if(!project->isEmpty("IMAGES")) + t << varGlue("QMAKE_IMAGE_COLLECTION", "\n\t-$(DEL_FILE) ", "\n\t-$(DEL_FILE) ", ""); + t << endl; + + t << "distclean: clean" + << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" + << endl << endl; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/borland_bmake.h b/qmake/generators/win32/borland_bmake.h new file mode 100644 index 0000000..46c7d0a --- /dev/null +++ b/qmake/generators/win32/borland_bmake.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BORLAND_BMAKE_H +#define BORLAND_BMAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class BorlandMakefileGenerator : public Win32MakefileGenerator +{ + bool init_flag; + void writeBorlandParts(QTextStream &); + void writeBuildRulesPart(QTextStream &t); + void writeCleanParts(QTextStream &t); + bool writeMakefile(QTextStream &); + void init(); + +public: + BorlandMakefileGenerator(); + ~BorlandMakefileGenerator(); +}; + +inline BorlandMakefileGenerator::~BorlandMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // BORLAND_BMAKE_H diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp new file mode 100644 index 0000000..7473b10 --- /dev/null +++ b/qmake/generators/win32/mingw_make.cpp @@ -0,0 +1,460 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mingw_make.h" +#include "option.h" +#include "meta.h" +#include <qregexp.h> +#include <qdir.h> +#include <stdlib.h> +#include <time.h> + +QT_BEGIN_NAMESPACE + +MingwMakefileGenerator::MingwMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + if (Option::shellPath.isEmpty()) + quote = "\""; + else + quote = "'"; +} + +bool MingwMakefileGenerator::isWindowsShell() const +{ +#ifdef Q_OS_WIN + return Option::shellPath.isEmpty(); +#else + return Win32MakefileGenerator::isWindowsShell(); +#endif +} + +QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const +{ + QString ret = path; + ret.remove('\"'); + ret.replace('\\', "/"); + ret.replace(' ', "\\ "); + return ret; +} + +QString MingwMakefileGenerator::getLibTarget() +{ + return QString("lib" + project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".a"); +} + +bool MingwMakefileGenerator::findLibraries() +{ + QStringList &l = project->values("QMAKE_LIBS"); + + QList<QMakeLocalFileName> dirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); + libpathit != libpaths.end(); ++libpathit) + dirs.append(QMakeLocalFileName((*libpathit))); + } + + QStringList::Iterator it = l.begin(); + while (it != l.end()) { + if ((*it).startsWith("-l")) { + QString steam = (*it).mid(2), out; + QString suffix; + if (!project->isEmpty("QMAKE_" + steam.toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + steam.toUpper() + "_SUFFIX"); + for (QList<QMakeLocalFileName>::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { + QString extension; + int ver = findHighestVersion((*dir_it).local(), steam, "dll.a|a"); + if (ver != -1) + extension += QString::number(ver); + extension += suffix; + if(QMakeMetaInfo::libExists((*dir_it).local() + Option::dir_sep + steam) || + exists((*dir_it).local() + Option::dir_sep + steam + extension + ".a") || + exists((*dir_it).local() + Option::dir_sep + steam + extension + ".dll.a")) { + out = (*it) + extension; + break; + } + } + if (!out.isEmpty()) // We assume if it never finds it that its correct + (*it) = out; + } else if((*it).startsWith("-L")) { + dirs.append(QMakeLocalFileName((*it).mid(2))); + } + + ++it; + } + return true; +} + +bool MingwMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + t << "all clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + writeMakeQmake(t); + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { + if(Option::mkfile::do_stub_makefile) { + t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("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" << endl + << "qmake_all:" << endl; + writeMakeQmake(t); + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; + return true; + } + writeMingwParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; + } + +void createLdObjectScriptFile(const QString &fileName, const QStringList &objList) +{ + QString filePath = Option::output_dir + QDir::separator() + fileName; + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream t(&file); + t << "INPUT(" << endl; + for (QStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { + if (QDir::isRelativePath(*it)) + t << "./" << *it << endl; + else + t << *it << endl; + } + t << ");" << endl; + t.flush(); + file.close(); + } +} + +void createArObjectScriptFile(const QString &fileName, const QString &target, const QStringList &objList) +{ + QString filePath = Option::output_dir + QDir::separator() + fileName; + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream t(&file); + t << "CREATE " << target << endl; + for (QStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { + if (QDir::isRelativePath(*it)) + t << "ADDMOD " << *it << endl; + else + t << *it << endl; + } + t << "SAVE" << endl; + t.flush(); + file.close(); + } +} + +void MingwMakefileGenerator::writeMingwParts(QTextStream &t) +{ + writeStandardParts(t); + + if (!preCompHeaderOut.isEmpty()) { + QString header = project->first("PRECOMPILED_HEADER"); + QString cHeader = preCompHeaderOut + Option::dir_sep + "c"; + t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " " + << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(preCompHeaderOut) + << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header + << endl << endl; + QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++"; + t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " " + << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(preCompHeaderOut) + << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header + << endl << endl; + } +} + +void MingwMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + if(project->values("MAKEFILE").isEmpty()) + project->values("MAKEFILE").append("Makefile"); + if(project->values("QMAKE_QMAKE").isEmpty()) + project->values("QMAKE_QMAKE").append("qmake"); + return; + } + + project->values("TARGET_PRL").append(project->first("TARGET")); + + processVars(); + + if (!project->values("RES_FILE").isEmpty()) { + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("RES_FILE")); + } + + // LIBS defined in Profile comes first for gcc + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + + QString targetfilename = project->values("TARGET").first(); + QStringList &configs = project->values("CONFIG"); + + if(project->isActiveConfig("qt_dll")) + if(configs.indexOf("qt") == -1) + configs.append("qt"); + + if(project->isActiveConfig("dll")) { + QString destDir = ""; + if(!project->first("DESTDIR").isEmpty()) + destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false); + project->values("MINGW_IMPORT_LIB").prepend(destDir + "lib" + project->first("TARGET") + + project->first("TARGET_VERSION_EXT") + ".a"); + project->values("QMAKE_LFLAGS").append(QString("-Wl,--out-implib,") + project->first("MINGW_IMPORT_LIB")); + } + + if(!project->values("DEF_FILE").isEmpty()) + project->values("QMAKE_LFLAGS").append(QString("-Wl,") + project->first("DEF_FILE")); + + MakefileGenerator::init(); + + // precomp + if (!project->first("PRECOMPILED_HEADER").isEmpty() + && project->isActiveConfig("precompile_header")) { + QString preCompHeader = var("PRECOMPILED_DIR") + + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName(); + preCompHeaderOut = preCompHeader + ".gch"; + project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c"); + project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++"); + + project->values("QMAKE_RUN_CC").clear(); + project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader + + " $(CFLAGS) $(INCPATH) -o $obj $src"); + project->values("QMAKE_RUN_CC_IMP").clear(); + project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader + + " $(CFLAGS) $(INCPATH) -o $@ $<"); + project->values("QMAKE_RUN_CXX").clear(); + project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader + + " $(CXXFLAGS) $(INCPATH) -o $obj $src"); + project->values("QMAKE_RUN_CXX_IMP").clear(); + project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader + + " $(CXXFLAGS) $(INCPATH) -o $@ $<"); + } + + if(project->isActiveConfig("dll")) { + project->values("QMAKE_CLEAN").append(project->first("MINGW_IMPORT_LIB")); + } +} + +void MingwMakefileGenerator::fixTargetExt() +{ + if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + project->values("TARGET_EXT").append(".a"); + project->values("QMAKE_LFLAGS").append("-static"); + project->values("TARGET").first() = "lib" + project->first("TARGET"); + } else { + Win32MakefileGenerator::fixTargetExt(); + } +} + +void MingwMakefileGenerator::writeIncPart(QTextStream &t) +{ + t << "INCPATH = "; + + QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) { + QString inc = (*incit); + inc.replace(QRegExp("\\\\$"), ""); + inc.replace(QRegExp("\""), ""); + t << "-I" << quote << inc << quote << " "; + } + t << "-I" << quote << specdir() << quote + << endl; +} + +void MingwMakefileGenerator::writeLibsPart(QTextStream &t) +{ + if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + t << "LIB = " << var("QMAKE_LIB") << endl; + } else { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl; + t << "LIBS = "; + if(!project->values("QMAKE_LIBDIR").isEmpty()) + writeLibDirPart(t); + t << var("QMAKE_LIBS").replace(QRegExp("(\\slib|^lib)")," -l") << endl; + } +} + +void MingwMakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + for (int i = 0; i < libDirs.size(); ++i) + libDirs[i].remove("\""); + t << valGlue(libDirs,"-L"+quote,quote+" -L" +quote,quote) << " "; +} + +void MingwMakefileGenerator::writeObjectsPart(QTextStream &t) +{ + if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) { + objectsLinkLine = "$(OBJECTS)"; + } else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + QString ar_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); + if (!var("BUILD_NAME").isEmpty()) { + ar_script_file += "." + var("BUILD_NAME"); + } + createArObjectScriptFile(ar_script_file, var("DEST_TARGET"), project->values("OBJECTS")); + objectsLinkLine = "ar -M < " + ar_script_file; + } else { + QString ld_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); + if (!var("BUILD_NAME").isEmpty()) { + ld_script_file += "." + var("BUILD_NAME"); + } + createLdObjectScriptFile(ld_script_file, project->values("OBJECTS")); + objectsLinkLine = ld_script_file; + } + Win32MakefileGenerator::writeObjectsPart(t); +} + +void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS"))," "," "," ") << " $(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <<var("QMAKE_PRE_LINK"); + if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) { + t << "\n\t" << "$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ; + } else { + t << "\n\t" << objectsLinkLine << " " ; + } + } else { + t << "\n\t" << "$(LINK) $(LFLAGS) -o $(DESTDIR_TARGET) " << objectsLinkLine << " " << " $(LIBS)"; + } + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" <<var("QMAKE_POST_LINK"); + t << endl; +} + +void MingwMakefileGenerator::writeRcFilePart(QTextStream &t) +{ + const QString rc_file = fileFixify(project->first("RC_FILE")); + + QString incPathStr = fileInfo(rc_file).path(); + if (incPathStr != "." && QDir::isRelativePath(incPathStr)) + incPathStr.prepend("./"); + + if (!rc_file.isEmpty()) { + t << escapeDependencyPath(var("RES_FILE")) << ": " << rc_file << "\n\t" + << var("QMAKE_RC") << " -i " << rc_file << " -o " << var("RES_FILE") + << " --include-dir=" << incPathStr << endl << endl; + } +} + +void MingwMakefileGenerator::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) { + out.removeAll((*it)); + out.append((*it)); + } + } else { + Win32MakefileGenerator::processPrlVariable(var, l); + } +} + +QStringList &MingwMakefileGenerator::findDependencies(const QString &file) +{ + QStringList &aList = MakefileGenerator::findDependencies(file); + // Note: The QMAKE_IMAGE_COLLECTION file have all images + // as dependency, so don't add precompiled header then + if (file == project->first("QMAKE_IMAGE_COLLECTION") + || preCompHeaderOut.isEmpty()) + return aList; + for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) { + if (file.endsWith(*it)) { + QString cHeader = preCompHeaderOut + Option::dir_sep + "c"; + if (!aList.contains(cHeader)) + aList += cHeader; + break; + } + } + for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) { + if (file.endsWith(*it)) { + QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++"; + if (!aList.contains(cppHeader)) + aList += cppHeader; + break; + } + } + return aList; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/mingw_make.h b/qmake/generators/win32/mingw_make.h new file mode 100644 index 0000000..1c394b2 --- /dev/null +++ b/qmake/generators/win32/mingw_make.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MINGW_MAKE_H +#define MINGW_MAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class MingwMakefileGenerator : public Win32MakefileGenerator +{ +public: + MingwMakefileGenerator(); + ~MingwMakefileGenerator(); +protected: + QString escapeDependencyPath(const QString &path) const; + QString getLibTarget(); +private: + bool isWindowsShell() const; + void writeMingwParts(QTextStream &); + void writeIncPart(QTextStream &t); + void writeLibsPart(QTextStream &t); + void writeLibDirPart(QTextStream &t); + bool writeMakefile(QTextStream &); + void writeObjectsPart(QTextStream &t); + void writeBuildRulesPart(QTextStream &t); + void writeRcFilePart(QTextStream &t); + void init(); + void processPrlVariable(const QString &var, const QStringList &l); + + QStringList &findDependencies(const QString &file); + + QString preCompHeaderOut; + + virtual bool findLibraries(); + void fixTargetExt(); + + bool init_flag; + QString objectsLinkLine; + QString quote; +}; + +inline MingwMakefileGenerator::~MingwMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MINGW_MAKE_H diff --git a/qmake/generators/win32/msvc_dsp.cpp b/qmake/generators/win32/msvc_dsp.cpp new file mode 100644 index 0000000..17a5154 --- /dev/null +++ b/qmake/generators/win32/msvc_dsp.cpp @@ -0,0 +1,1207 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_dsp.h" +#include "option.h" + +#include <qdir.h> +#include <qset.h> + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +DspMakefileGenerator::DspMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ +} + +bool DspMakefileGenerator::writeMakefile(QTextStream &t) +{ + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + /* for now just dump, I need to generated an empty dsp 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; + } + + // Generate workspace file + if(project->first("TEMPLATE") == "vcsubdirs") { + if (!project->isActiveConfig("build_pass")) { + debug_msg(1, "Generator: MSVC: Writing workspave file"); + writeSubDirs(t); + } else { + debug_msg(1, "Generator: MSVC: Not writing workspace file for build_pass configs"); + } + return true; + } else if (project->first("TEMPLATE") == "vcapp" || project->first("TEMPLATE") == "vclib") { + if(!project->isActiveConfig("build_pass")) + return writeDspParts(t); + return true; + } + return project->isActiveConfig("build_pass"); +} + +bool DspMakefileGenerator::hasBuiltinCompiler(const QString &filename) const +{ + for (int i = 0; i < Option::cpp_ext.count(); ++i) + if (filename.endsWith(Option::cpp_ext.at(i))) + return true; + for (int i = 0; i < Option::c_ext.count(); ++i) + if (filename.endsWith(Option::c_ext.at(i))) + return true; + return false; +} + +QString DspMakefileGenerator::replaceExtraCompilerVariables(const QString &var, const QStringList &in, const QStringList &out) +{ + QString ret = MakefileGenerator::replaceExtraCompilerVariables(var, in, out); + ret.replace("$(DEFINES)", varGlue("PRL_EXPORT_DEFINES"," -D"," -D","") + + varGlue("DEFINES"," -D"," -D","")); + + QString incpath = this->var("MSVCDSP_INCPATH"); + incpath.replace("/I", "-I"); + ret.replace("$(INCPATH)", incpath); + return ret; +} + + +// if config is part of a multibuild thenthe gule (this) has the correct MSVCDSP_PROJECT +QString DspMakefileGenerator::configName(DspMakefileGenerator * config) +{ + return var("MSVCDSP_PROJECT") + config->var("MSVCDSP_CONFIG_NAME"); +} + +bool DspMakefileGenerator::writeDspHeader(QTextStream &t) +{ + DspMakefileGenerator * config = this; + if (mergedProjects.count()) + config = mergedProjects.at(0); + + t << "# Microsoft Developer Studio Project File - Name=\"" << var("MSVCDSP_PROJECT") << "\" - Package Owner=<4>" << endl; + t << "# Microsoft Developer Studio Generated Build File, Format Version 6.00" << endl; + t << "# ** DO NOT EDIT **" << endl; + t << endl; + t << "# TARGTYPE \"Win32 (x86) " << var("MSVCDSP_TARGETTYPE") << "\" " << var("MSVCDSP_DSPTYPE") << endl; + t << endl; + t << "CFG=\"" << configName(config) << "\"" << endl; + t << "!MESSAGE This is not a valid makefile. To build this project using NMAKE," << endl; + t << "!MESSAGE use the Export Makefile command and run" << endl; + t << "!MESSAGE " << endl; + t << "!MESSAGE NMAKE /f " << escapeFilePath(var("TARGET")) << ".mak." << endl; + t << "!MESSAGE " << endl; + t << "!MESSAGE You can specify a configuration when running NMAKE" << endl; + t << "!MESSAGE by defining the macro CFG on the command line. For example:" << endl; + t << "!MESSAGE " << endl; + t << "!MESSAGE NMAKE /f " << escapeFilePath(var("TARGET")) << ".mak CFG=\"" << configName(config) << "\"" << endl; + t << "!MESSAGE " << endl; + t << "!MESSAGE Possible choices for configuration are:" << endl; + t << "!MESSAGE " << endl; + if (mergedProjects.count()) { + for (int i = 0; i < mergedProjects.count(); ++i) { + DspMakefileGenerator * config = mergedProjects.at(i); + t << "!MESSAGE \"" << configName(config) << "\" (based on \"Win32 (x86) " << config->var("MSVCDSP_TARGETTYPE") << "\")" << endl; + } + } else { + t << "!MESSAGE \"" << configName(config) << "\" (based on \"Win32 (x86) " << config->var("MSVCDSP_TARGETTYPE") << "\")" << endl; + } + t << "!MESSAGE " << endl; + t << endl; + t << "# Begin Project" << endl; + t << "# PROP AllowPerConfigDependencies 0" << endl; + t << "# PROP Scc_ProjName \"\"" << endl; + t << "# PROP Scc_LocalPath \"\"" << endl; + t << "CPP=" << config->var("QMAKE_CC") << endl; + t << "MTL=" << config->var("QMAKE_IDL") << endl; + t << "RSC=" << config->var("QMAKE_RC") << endl; + t << "BSC32=bscmake.exe" << endl; + + return true; +} + + +bool DspMakefileGenerator::writeDspParts(QTextStream &t) +{ + //bool staticLibTarget = var("MSVCDSP_DSPTYPE") == "0x0104"; + + writeDspHeader(t); + writeDspConfig(t, this); + t << endl; + t << "# Begin Target" << endl; + t << endl; + t << "# Name \"" << configName(this) << "\"" << endl; + t << endl; + + + QStringList listNames = QString("SOURCES|DEF_FILE").split("|"); + QStringList allListNames = listNames; + writeFileGroup(t, listNames, "Source Files", "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"); + listNames = QStringList("HEADERS"); + allListNames += listNames; + writeFileGroup(t, QStringList("HEADERS"), "Header Files", "h;hpp;hxx;hm;inl"); + listNames = QString("FORMS|INTERFACES|FORMS3").split("|"); + allListNames += listNames; + writeFileGroup(t, listNames, "Form Files", "ui"); + listNames = QStringList("IMAGES"); + allListNames += listNames; + writeFileGroup(t, QStringList("IMAGES"), "Image Files", ""); + listNames = QString("RC_FILE|RESOURCES").split("|"); + allListNames += listNames; + writeFileGroup(t, listNames, "Resources", "rc;qrc"); + listNames = QStringList("TRANSLATIONS"); + allListNames += listNames; + writeFileGroup(t, listNames, "Translations", "ts;xlf"); + listNames = QStringList("LEXSOURCES"); + allListNames += listNames; + writeFileGroup(t, listNames, "Lexables", "l"); + listNames = QStringList("YACCSOURCES"); + allListNames += listNames; + writeFileGroup(t, listNames, "Yaccables", "y"); + listNames = QStringList("TYPELIBS"); + allListNames += listNames; + writeFileGroup(t, listNames, "Type Libraries", "tlb;olb"); + + 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 &inputs = project->values((*it)+".input"); + for (QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) { + if (!allListNames.contains((*input)) && *input != "UIC3_HEADERS") + writeFileGroup(t, QStringList((*input)), (*input) + " Files", ""); + } + } + } + + project->values("SWAPPED_BUILD_STEPS") = swappedBuildSteps.keys(); + + writeFileGroup(t, QString("GENERATED_SOURCES|GENERATED_FILES|SWAPPED_BUILD_STEPS").split("|"), "Generated", ""); + + t << "# End Target" << endl; + t << "# End Project" << endl; + return true; +} + +void +DspMakefileGenerator::init() +{ + if(init_flag) + return; + QStringList::Iterator it; + init_flag = true; + + platform = "Win32"; + if(!project->values("QMAKE_PLATFORM").isEmpty()) + platform = varGlue("QMAKE_PLATFORM", "", " ", ""); + + // this should probably not be here, but I'm using it to wrap the .t files + if(project->first("TEMPLATE") == "vcapp") + project->values("QMAKE_APP_FLAG").append("1"); + else if(project->first("TEMPLATE") == "vclib") + project->values("QMAKE_LIB_FLAG").append("1"); + + if(project->values("QMAKESPEC").isEmpty()) + project->values("QMAKESPEC").append(qgetenv("QMAKESPEC")); + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + processVars(); + + if(!project->values("VERSION").isEmpty()) { + QString version = project->values("VERSION").first(); + int firstDot = version.indexOf("."); + QString major = version.left(firstDot); + QString minor = version.right(version.length() - firstDot - 1); + minor.replace(".", ""); + project->values("MSVCDSP_LFLAGS").append("/VERSION:" + major + "." + minor); + } + + QString msvcdsp_project; + if(!project->isEmpty("TARGET")) { + project->values("TARGET") = unescapeFilePaths(project->values("TARGET")); + msvcdsp_project = project->first("TARGET"); + } + + MakefileGenerator::init(); + + if(msvcdsp_project.isEmpty()) + msvcdsp_project = Option::output.fileName(); + + msvcdsp_project = msvcdsp_project.right(msvcdsp_project.length() - msvcdsp_project.lastIndexOf("\\") - 1); + int dotFind = msvcdsp_project.lastIndexOf("."); + if(dotFind != -1) + msvcdsp_project = msvcdsp_project.left(dotFind); + msvcdsp_project.replace("-", ""); + + project->values("MSVCDSP_PROJECT").append(msvcdsp_project); + + QStringList &proj = project->values("MSVCDSP_PROJECT"); + + for(QStringList::Iterator it = proj.begin(); it != proj.end(); ++it) + (*it).replace(QRegExp("\\.[a-zA-Z0-9_]*$"), ""); + + if(!project->values("QMAKE_APP_FLAG").isEmpty()) { + if(project->isActiveConfig("console")) { + project->values("MSVCDSP_TARGETTYPE").append("Console Application"); + project->values("MSVCDSP_DSPTYPE").append("0x0103"); + project->values("MSVCDSP_DEFINES").append(" /D \"_CONSOLE\" "); + } else { + project->values("MSVCDSP_TARGETTYPE").append("Application"); + project->values("MSVCDSP_DSPTYPE").append("0x0101"); + project->values("MSVCDSP_DEFINES").append(" /D \"_WINDOWS\" "); + } + } else { + if(project->isActiveConfig("dll")) { + project->values("MSVCDSP_TARGETTYPE").append("Dynamic-Link Library"); + project->values("MSVCDSP_DSPTYPE").append("0x0102"); + project->values("MSVCDSP_DEFINES").append(" /D \"_USRDLL\" "); + } else { + project->values("MSVCDSP_TARGETTYPE").append("Static Library"); + project->values("MSVCDSP_DSPTYPE").append("0x0104"); + project->values("MSVCDSP_DEFINES").append(" /D \"_LIB\" "); + } + } + + project->values("MSVCDSP_LFLAGS") += project->values("QMAKE_LFLAGS"); + + if(!project->values("QMAKE_LIBDIR").isEmpty()) + project->values("MSVCDSP_LFLAGS").append(valGlue( + escapeFilePaths(project->values("QMAKE_LIBDIR")), + "/LIBPATH:"," /LIBPATH:","")); + + project->values("MSVCDSP_DEFINES").append(varGlue("DEFINES","/D ","" " /D ","")); + project->values("MSVCDSP_DEFINES").append(varGlue("PRL_EXPORT_DEFINES","/D ","" " /D ","")); + project->values("MSVCDSP_DEFINES").append(" /D \"WIN32\" "); + + QStringList &libs = project->values("QMAKE_LIBS"); + for(QStringList::Iterator libit = libs.begin(); libit != libs.end(); ++libit) { + project->values("MSVCDSP_LIBS").append(" " + escapeFilePath(*libit)); + } + + QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) { + QString inc = (*incit); + project->values("MSVCDSP_INCPATH").append("/I" + escapeFilePath(inc)); + } + project->values("MSVCDSP_INCPATH").append("/I" + escapeFilePath(specdir())); + + QString dest; + QString preLinkStep; + QString postLinkStep; + QString copyDllStep; + + if(!project->values("QMAKE_PRE_LINK").isEmpty()) + preLinkStep += var("QMAKE_PRE_LINK"); + + if(!project->values("QMAKE_POST_LINK").isEmpty()) + postLinkStep += var("QMAKE_POST_LINK"); + + // don't destroy the target, it is used by prl writer. + if(!project->values("DESTDIR").isEmpty()) { + dest = project->first("DESTDIR"); + project->values("DESTDIR").first() = dest; + dest = project->values("TARGET").first() + project->first("TARGET_EXT"); + dest.prepend(project->first("DESTDIR")); + Option::fixPathToTargetOS(dest); + dest = escapeFilePath(dest); + + project->values("MSVCDSP_TARGET").append( + QString("/out:") + dest); + if(project->isActiveConfig("dll")) { + QString imp = dest; + imp.replace(".dll", ".lib"); + project->values("MSVCDSP_TARGET").append(QString(" /implib:") + escapeFilePath(imp)); + } + } + + if(project->isActiveConfig("dll") && !project->values("DLLDESTDIR").isEmpty()) { + QStringList dlldirs = project->values("DLLDESTDIR"); + if(dlldirs.count()) + copyDllStep += "\t"; + for(QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { + copyDllStep += "copy \"$(TargetPath)\" " + escapeFilePath(Option::fixPathToTargetOS(*dlldir)) + "\t"; + } + } + + if(!preLinkStep.isEmpty()) { + project->values("MSVCDSP_PRE_LINK").append( + "# Begin Special Build Tool\n" + "SOURCE=$(InputPath)\n" + "PreLink_Desc=Post Build Step\n" + "PreLink_Cmds=" + preLinkStep + "\n" + "# End Special Build Tool\n"); + } + + if(!postLinkStep.isEmpty() || !copyDllStep.isEmpty()) { + project->values("MSVCDSP_POST_LINK").append( + "# Begin Special Build Tool\n" + "SOURCE=$(InputPath)\n" + "PostBuild_Desc=Post Build Step\n" + "PostBuild_Cmds=" + postLinkStep + copyDllStep + "\n" + "# End Special Build Tool\n"); + } + + QStringList &formList = project->values("FORMS"); + for(QStringList::ConstIterator hit = formList.begin(); hit != formList.end(); ++hit) { + if(exists(*hit + ".h")) + project->values("SOURCES").append(*hit + ".h"); + } + QStringList &form3List = project->values("FORMS3"); + for(QStringList::ConstIterator hit = form3List.begin(); hit != form3List.end(); ++hit) { + if(exists(*hit + ".h")) + project->values("SOURCES").append(*hit + ".h"); + } + + project->values("QMAKE_INTERNAL_PRL_LIBS") << "MSVCDSP_LIBS"; + + // Move some files around //### is this compat? + if (!project->values("IMAGES").isEmpty()) { + QString imageFactory(project->first("QMAKE_IMAGE_COLLECTION")); + project->values("GENERATED_SOURCES") += imageFactory; + project->values("SOURCES").removeAll(imageFactory); + } + + // Setup PCH variables + precompH = project->first("PRECOMPILED_HEADER"); + namePCH = fileInfo(precompH).fileName(); + usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); + if (usePCH) { + // Created files + precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext; + precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch"; + + // Add PRECOMPILED_HEADER to HEADERS + if (!project->values("HEADERS").contains(precompH)) + project->values("HEADERS") += precompH; + // Add precompile compiler options + project->values("PRECOMPILED_FLAGS") = QStringList("/Fp" + precompPch + " /Yu" + escapeFilePath(namePCH) + " /FI" + escapeFilePath(namePCH) + " "); + // Return to variable pool + project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); + project->values("PRECOMPILED_PCH") = QStringList(precompPch); + } + + QString buildName; + if (!var("BUILD_NAME").isEmpty()) + buildName = var("BUILD_NAME"); + else if (project->isActiveConfig("debug")) + buildName = "Debug"; + else + buildName = "Release"; + + project->values("MSVCDSP_CONFIG_NAME") = QStringList(" - " + platform + " " + buildName); +} + +void DspMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l) +{ + if(var == "QMAKE_PRL_DEFINES") { + QStringList &out = project->values("MSVCDSP_DEFINES"); + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(out.indexOf((*it)) == -1) + out.append((" /D \"" + *it + "\"")); + } + } else { + MakefileGenerator::processPrlVariable(var, l); + } +} + +bool DspMakefileGenerator::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() + QDir::separator(); + } + + if(!outdir.isEmpty() || file.fileName().isEmpty()) { + QString ext = project->first("DSP_EXTENSION"); + if(project->first("TEMPLATE") == "vcsubdirs") { + if (!project->first("DSW_EXTENSION").isEmpty()) + ext = project->first("DSW_EXTENSION"); + else + ext = ".dsw"; + } + QString outputName = unescapeFilePath(project->first("QMAKE_DSP_PROJECT_NAME")); + if (!project->first("MAKEFILE").isEmpty()) + outputName = unescapeFilePath(project->first("MAKEFILE")); + if (outputName.isEmpty()) + outputName = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + file.setFileName(outdir + outputName + ext); + } + + if(QDir::isRelativePath(file.fileName())) { + QString ofile = Option::fixPathToLocalOS(file.fileName()); + int slashfind = ofile.lastIndexOf(Option::dir_sep); + if(slashfind == -1) { + ofile = ofile.replace(QRegExp("-"), "_"); + } else { + int hypenfind = ofile.indexOf('-', slashfind); + while (hypenfind != -1 && slashfind < hypenfind) { + ofile = ofile.replace(hypenfind, 1, "_"); + hypenfind = ofile.indexOf('-', hypenfind + 1); + } + } + file.setFileName(Option::fixPathToLocalOS(qmake_getpwd() + Option::dir_sep + ofile)); + } + return Win32MakefileGenerator::openOutput(file, build); +} + +bool DspMakefileGenerator::mergeBuildProject(MakefileGenerator *other) +{ + + mergedProjects.prepend(static_cast<DspMakefileGenerator*>(other)); + return true; +} + +bool DspMakefileGenerator::writeProjectMakefile() +{ + bool ret = true; + + QTextStream t(&Option::output); + // Check if all requirements are fulfilled + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return true; + } + + // Generate project file + if(project->first("TEMPLATE") == "vcapp" || + project->first("TEMPLATE") == "vclib") { + if (!mergedProjects.count()) { + warn_msg(WarnLogic, "Generator: MSVC DSP: no single configuration created, cannot output project!"); + return false; + } + debug_msg(1, "Generator: MSVC 6: Writing project file"); + + writeDspHeader(t); + for (int i = 0; i < mergedProjects.count(); ++i) { + DspMakefileGenerator* config = mergedProjects.at(i); + t << endl; + if (i == 0) + t << "!IF"; + else + t << "!ELSEIF"; + t << " \"$(CFG)\" == \"" << configName(config) << "\"" << endl; + t << endl; + writeDspConfig(t, config); + } + t << endl; + t << "!ENDIF " << endl; + t << endl; + t << "# Begin Target" << endl; + t << endl; + for (int i = 0; i < mergedProjects.count(); ++i) + t << "# Name \"" << configName(mergedProjects.at(i)) << "\"" << endl; + t << endl; + + QMap< QString, QSet<QString> > files; + + // merge source files + for (int i = 0; i < mergedProjects.count(); ++i) { + + DspMakefileGenerator* config = mergedProjects.at(i); + + files["DEF_FILE"] += config->project->values("DEF_FILE").toSet(); + files["SOURCES"] += config->project->values("SOURCES").toSet(); + files["HEADERS"] += config->project->values("HEADERS").toSet(); + files["INTERFACES"] += config->project->values("INTERFACES").toSet(); + files["FORMS"] += config->project->values("FORMS").toSet(); + files["FORMS"] += config->project->values("FORMS3").toSet(); + files["IMAGES"] += config->project->values("IMAGES").toSet(); + files["RC_FILE"] += config->project->values("RC_FILE").toSet(); + files["RESOURCES"] += config->project->values("RESOURCES").toSet(); + files["TRANSLATIONS"] += config->project->values("TRANSLATIONS").toSet(); + files["LEXSOURCES"] += config->project->values("LEXSOURCES").toSet(); + files["YACCSOURCES"] += config->project->values("YACCSOURCES").toSet(); + files["TYPELIBS"] += config->project->values("TYPELIBS").toSet(); + + if (!config->project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = config->project->values("QMAKE_EXTRA_COMPILERS"); + for (QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const QStringList &inputs = project->values((*it)+".input"); + for (QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) { + if (*input != "UIC3_HEADERS") + files[(*input)] += config->project->values((*input)).toSet(); + } + } + } + } + + QStringList keys = files.keys(); + for (int k = 0; k < keys.size(); ++k) + project->values(keys.at(k)) = QList<QString>::fromSet(files[keys.at(k)]); + + QStringList listNames = QString("SOURCES|DEF_FILE").split("|"); + QStringList allListNames = listNames; + writeFileGroup(t, listNames, "Source Files", "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"); + listNames = QStringList("HEADERS"); + allListNames += listNames; + writeFileGroup(t, listNames, "Header Files", "h;hpp;hxx;hm;inl"); + listNames = QString("FORMS|INTERFACES|FORMS3").split("|"); + allListNames += listNames; + writeFileGroup(t, listNames, "Form Files", "ui"); + listNames = QStringList("IMAGES"); + allListNames += listNames; + writeFileGroup(t, listNames, "Image Files", ""); + listNames = QString("RC_FILE|RESOURCES").split("|"); + allListNames += listNames; + writeFileGroup(t, listNames, "Resources", "rc;qrc"); + listNames = QStringList("TRANSLATIONS"); + allListNames += listNames; + writeFileGroup(t, listNames, "Translations", "ts;xlf"); + listNames = QStringList("LEXSOURCES"); + allListNames += listNames; + writeFileGroup(t, listNames, "Lexables", "l"); + listNames = QStringList("YACCSOURCES"); + allListNames += listNames; + writeFileGroup(t, listNames, "Yaccables", "y"); + listNames = QStringList("TYPELIBS"); + allListNames += listNames; + writeFileGroup(t, listNames, "Type Libraries", "tlb;olb"); + + for (int l = 0; l < allListNames.size(); ++l) + keys.removeAll(allListNames.at(l)); + + for (int k = 0; k < keys.size(); ++k) + writeFileGroup(t, QStringList(keys.at(k)), keys.at(k) + " Files", ""); + + // done last as generated may have changed when creating build rules for the above + for (int i = 0; i < mergedProjects.count(); ++i) { + + DspMakefileGenerator* config = mergedProjects.at(i); + + config->project->values("SWAPPED_BUILD_STEPS") = config->swappedBuildSteps.keys(); + files["SWAPPED_BUILD_STEPS"] += config->project->values("SWAPPED_BUILD_STEPS").toSet(); + + files["GENERATED_SOURCES"] += config->project->values("GENERATED_SOURCES").toSet(); + files["GENERATED_FILES"] += config->project->values("GENERATED_FILES").toSet(); + } + + project->values("SWAPPED_BUILD_STEPS") = QList<QString>::fromSet(files["SWAPPED_BUILD_STEPS"]); + project->values("GENERATED_SOURCES") = QList<QString>::fromSet(files["GENERATED_SOURCES"]); + project->values("GENERATED_FILES") = QList<QString>::fromSet(files["GENERATED_FILES"]); + + writeFileGroup(t, QString("GENERATED_SOURCES|GENERATED_FILES|SWAPPED_BUILD_STEPS").split("|"), "Generated", ""); + t << endl; + t << "# End Target" << endl; + t << "# End Project" << endl; + }else if(project->first("TEMPLATE") == "vcsubdirs") { + ret = writeMakefile(t); + } + + return ret; +} + +const char _dswHeader60[] = "Microsoft Developer Studio Workspace File, Format Version 6.00\n"; +const char _dswWarning[] = "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n"; +const char _dswDevider[] = "###############################################################################\n"; +const char _dswProjectName[] = "Project: \"%1\"=%2 - Package Owner=<4>\n"; // %1 = project name, %2 = project path +const char _dswPackage5Start[] = "Package=<5>\n{{{\n"; +const char _dswPackage5Stop[] = "}}}\n"; +const char _dswPackage4Start[] = "Package=<4>\n{{{\n"; +const char _dswPackage4Stop[] = "}}}\n"; +const char _dswProjectDep[] = " Begin Project Dependency\n Project_Dep_Name %1\n End Project Dependency\n"; // %1 = project name +const char _dswGlobal[] = "Global:\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<3>\n{{{\n}}}\n\n"; + + +struct WorkspaceDepend { + QString dspProjectFile, orig_target, target; + QStringList dependencies; +}; + +void DspMakefileGenerator::writeSubDirs(QTextStream &t) +{ + // Output headers + t << _dswHeader60; + t << _dswWarning; + t << endl; + + QHash<QString, WorkspaceDepend*> workspace_depends; + QList<WorkspaceDepend*> workspace_cleanup; + QStringList subdirs = project->values("SUBDIRS"); + QString oldpwd = qmake_getpwd(); + + // Make sure that all temp projects are configured + // for release so that the depends are created + // without the debug <lib>dxxx.lib name mangling + QStringList old_after_vars = Option::after_user_vars; + Option::after_user_vars.append("CONFIG+=release"); + + for(int i = 0; i < subdirs.size(); ++i) { + QString tmp = subdirs.at(i); + if(!project->isEmpty(tmp + ".file")) { + if(!project->isEmpty(tmp + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + tmp.toLatin1().constData()); + tmp = project->first(tmp + ".file"); + } else if(!project->isEmpty(tmp + ".subdir")) { + tmp = project->first(tmp + ".subdir"); + } + + 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() + ".pro"; + subdirs.append(profile); + } else { + 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)) { + // Check if all requirements are fulfilled + if(!tmp_proj.variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { + fprintf(stderr, "Project file(%s) not added to Workspace because all requirements not met:\n\t%s\n", + fn.toLatin1().constData(), tmp_proj.values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData()); + continue; + } + if(tmp_proj.first("TEMPLATE") == "vcsubdirs") { + QStringList tmp_proj_subdirs = tmp_proj.variables()["SUBDIRS"]; + for(int x = 0; x < tmp_proj_subdirs.size(); ++x) { + QString tmpdir = tmp_proj_subdirs.at(x); + if(!tmp_proj.isEmpty(tmpdir + ".file")) { + if(!tmp_proj.isEmpty(tmpdir + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + tmpdir.toLatin1().constData()); + tmpdir = tmp_proj.first(tmpdir + ".file"); + } else if(!tmp_proj.isEmpty(tmpdir + ".subdir")) { + tmpdir = tmp_proj.first(tmpdir + ".subdir"); + } + subdirs += fileFixify(tmpdir); + } + } else if(tmp_proj.first("TEMPLATE") == "vcapp" || tmp_proj.first("TEMPLATE") == "vclib") { + // Initialize a 'fake' project to get the correct variables + // and to be able to extract all the dependencies + DspMakefileGenerator tmp_dsp; + tmp_dsp.setNoIO(true); + tmp_dsp.setProjectFile(&tmp_proj); + if(Option::debug_level) { + 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()); + } + } + + // We assume project filename is [QMAKE_ORIG_TARGET].vcproj + QString dsp = unescapeFilePath(tmp_dsp.project->first("MSVCDSP_PROJECT") + project->first("DSP_EXTENSION")); + + // If file doesn't exsist, then maybe the users configuration + // doesn't allow it to be created. Skip to next... + if(!exists(qmake_getpwd() + Option::dir_sep + dsp)) { + warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(qmake_getpwd() + Option::dir_sep + dsp).toLatin1().constData()); + goto nextfile; // # Dirty! + } + + WorkspaceDepend *newDep = new WorkspaceDepend; + newDep->dspProjectFile = fileFixify(dsp); + newDep->orig_target = unescapeFilePath(tmp_proj.first("QMAKE_ORIG_TARGET")); + newDep->target = tmp_proj.first("MSVCDSP_PROJECT").section(Option::dir_sep, -1) + tmp_proj.first("TARGET_EXT"); + + // We want to store it as the .lib name. + if(newDep->target.endsWith(".dll")) + newDep->target = newDep->target.left(newDep->target.length()-3) + "lib"; + + // All projects having mocable sourcefiles are dependent on moc.exe + if(tmp_proj.variables()["CONFIG"].contains("moc")) + newDep->dependencies << "moc.exe"; + + // All extra compilers which has valid input are considered dependencies + const QStringList &quc = tmp_proj.variables()["QMAKE_EXTRA_COMPILERS"]; + for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) { + const QStringList &invar = tmp_proj.variables().value((*it) + ".input"); + for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { + const QStringList fileList = tmp_proj.variables().value(*iit); + if (!fileList.isEmpty()) { + QString dep = tmp_proj.first((*it) + ".commands").section('/', -1).section('\\', -1); + if (!newDep->dependencies.contains(dep)) + newDep->dependencies << dep; + } + } + } + + // Add all unknown libs to the deps + QStringList where("QMAKE_LIBS"); + if(!tmp_proj.isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = tmp_proj.variables()["QMAKE_INTERNAL_PRL_LIBS"]; + + for(QStringList::iterator wit = where.begin(); + wit != where.end(); ++wit) { + QStringList &l = tmp_proj.variables()[(*wit)]; + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString opt = (*it).trimmed(); + if(!opt.startsWith("/") && // Not a switch + opt != newDep->target && // Not self + opt != "opengl32.lib" && // We don't care about these libs + opt != "glu32.lib" && // to make depgen alittle faster + opt != "kernel32.lib" && + opt != "user32.lib" && + opt != "gdi32.lib" && + opt != "comdlg32.lib" && + opt != "advapi32.lib" && + opt != "shell32.lib" && + opt != "ole32.lib" && + opt != "oleaut32.lib" && + opt != "uuid.lib" && + opt != "imm32.lib" && + opt != "winmm.lib" && + opt != "wsock32.lib" && + opt != "ws2_32.lib" && + opt != "winspool.lib" && + opt != "delayimp.lib") + { + newDep->dependencies << opt.section(Option::dir_sep, -1); + } + } + } + workspace_cleanup.append(newDep); + workspace_depends.insert(newDep->target, newDep); + + debug_msg(1, "Generator: MSVC: Added project (name:'%s' path:'%s' deps:'%s')", + qPrintable(newDep->target) , qPrintable(newDep->dspProjectFile), + qPrintable(newDep->dependencies.join(";"))); + } + } +nextfile: + qmake_setpwd(oldpwd); + } + } + } + + // Restore previous after_user_var options + Option::after_user_vars = old_after_vars; + + // Output all projects + QString dswProjectName = QLatin1String(_dswProjectName); + QString dswProjectDep = QLatin1String(_dswProjectDep); + for(QList<WorkspaceDepend*>::Iterator it = workspace_cleanup.begin(); it != workspace_cleanup.end(); ++it) { + t << _dswDevider; + t << endl; + t << dswProjectName.arg((*it)->orig_target).arg((*it)->dspProjectFile); + t << endl; + t << _dswPackage5Start; + t << _dswPackage5Stop; + t << endl; + t << _dswPackage4Start; + + // Output project dependencies + for(QStringList::iterator dit = (*it)->dependencies.begin(); dit != (*it)->dependencies.end(); ++dit) { + if(WorkspaceDepend *vc = workspace_depends[*dit]) + t << dswProjectDep.arg(vc->orig_target); + } + + t << _dswPackage4Stop; + } + + // Output global part + t << _dswDevider << endl; + t << _dswGlobal; + t << _dswDevider; + t << endl << endl; +} + +class FolderGroup +{ +public: + QString name; + QString filter; + QMap<QString, FolderGroup *> subFolders; + QMap<QString, QString> files; + + void insertStructured(const QString &file, const QString &fileListName) + { + QStringList path = QFileInfo(file).path().split("/"); + if (!path.isEmpty() && path.at(0) == ".") + path.takeAt(0); + FolderGroup *currentFolder = this; + for (int i = 0; i < path.size(); i++) { + if (currentFolder->subFolders.contains(path.at(i))) { + currentFolder = currentFolder->subFolders.value(path.at(i)); + } else { + FolderGroup *newFolder = new FolderGroup; + newFolder->name = path.at(i); + currentFolder->subFolders.insert(path.at(i), newFolder); + currentFolder = newFolder; + } + } + currentFolder->files.insert(file, fileListName); + } + + void insertFlat(const QString &file, const QString &fileListName) + { + files.insert(file, fileListName); + } + + ~FolderGroup() + { + qDeleteAll(subFolders.values()); + } +}; + +bool DspMakefileGenerator::writeFileGroup(QTextStream &t, const QStringList &listNames, const QString &group, const QString &filter) +{ + FolderGroup root; + root.name = group; + root.filter = filter; + + for (int i = 0; i < listNames.count(); ++i) { + QStringList list = project->values(listNames.at(i)); + for (int j = 0; j < list.count(); ++j) { + const QString name = list.at(j); + if (name.isEmpty()) + continue; + if (project->isActiveConfig("flat")) + root.insertFlat(name, listNames.at(i)); + else + root.insertStructured(name, listNames.at(i)); + } + } + + if (root.files.isEmpty() && root.subFolders.isEmpty()) + return true; + + writeSubFileGroup(t, &root); + + return true; +} + +void DspMakefileGenerator::writeSubFileGroup(QTextStream &t, FolderGroup *folder) +{ + t << "# Begin Group \"" << folder->name << "\"" << endl; + t << "# PROP Default_Filter \"" << folder->filter << "\"" << endl; + QMap<QString, FolderGroup *>::const_iterator folderIt = folder->subFolders.begin(); + while (folderIt != folder->subFolders.end()) { + writeSubFileGroup(t, folderIt.value()); + ++folderIt; + } + QMap<QString, QString>::const_iterator it = folder->files.begin(); + while (it != folder->files.end()) { + t << "# Begin Source File" << endl; + t << "SOURCE=" << escapeFilePath(it.key()) << endl; + writeBuildstepForFile(t, it.key(), it.value()); + t << "# End Source File" << endl; + t << endl; + ++it; + } + t << "# End Group" << endl; + t << endl; +} + +bool DspMakefileGenerator::writeBuildstepForFile(QTextStream &t, const QString &file, const QString &listName) +{ + + if (!mergedProjects.count()) { + t << writeBuildstepForFileForConfig(file, listName, this); + return true; + } + + //only add special build rules when needed + + QStringList specialBuilds; + int i = 0; + for (i = 0; i < mergedProjects.count(); ++i) + specialBuilds += writeBuildstepForFileForConfig(file, listName, mergedProjects.at(i)); + + // no special build just return + if (specialBuilds.join("").isEmpty()) + return true; + + for (i = 0; i < mergedProjects.count(); ++i) { + if (i == 0) + t << "!IF"; + else + t << "!ELSEIF"; + t << " \"$(CFG)\" == \"" << configName(mergedProjects.at(i)) << "\"" << endl; + t << endl; + t << specialBuilds.at(i); + t << endl; + } + + t << "!ENDIF" << endl; + + return true; +} + +bool DspMakefileGenerator::writeDspConfig(QTextStream &t, DspMakefileGenerator *config) +{ + + bool isDebug = config->project->isActiveConfig("debug"); + bool staticLibTarget = config->var("MSVCDSP_DSPTYPE") == "0x0104"; + + QString outDir = Option::fixPathToTargetOS(config->project->first("DESTDIR")); + while (outDir.endsWith(Option::dir_sep)) + outDir.chop(1); + outDir = config->escapeFilePath(outDir); + + QString intDir = config->project->first("OBJECTS_DIR"); + while (intDir.endsWith(Option::dir_sep)) + intDir.chop(1); + intDir = config->escapeFilePath(intDir); + + t << "# PROP BASE Use_MFC 0" << endl; + t << "# PROP BASE Use_Debug_Libraries " << (isDebug ? "1" : "0") << endl; + t << "# PROP BASE Output_Dir " << outDir << endl; + t << "# PROP BASE Intermediate_Dir " << intDir << endl; + t << "# PROP BASE Target_Dir \"\"" << endl; + t << "# PROP Use_MFC 0" << endl; + t << "# PROP Use_Debug_Libraries " << (isDebug ? "1" : "0") << endl; + + t << "# PROP Output_Dir " << outDir << endl; + t << "# PROP Intermediate_Dir " << intDir << endl; + if (config->project->isActiveConfig("dll") || config->project->isActiveConfig("plugin")) + t << "# PROP Ignore_Export_Lib 1" << endl; + t << "# PROP Target_Dir \"\"" << endl; + t << "# ADD CPP " << config->var("MSVCDSP_INCPATH") << " /c /FD " << config->var("QMAKE_CXXFLAGS") << " " << config->var("MSVCDSP_DEFINES") << " " << config->var("PRECOMPILED_FLAGS") << endl; + t << "# ADD MTL /nologo /mktyplib203 /win32 /D " << (isDebug ? "\"_DEBUG\"" : "\"NDEBUG\"") << endl; + t << "# ADD RSC /l 0x409 /d " << (isDebug ? "\"_DEBUG\"" : "\"NDEBUG\"") << endl; + t << "# ADD BSC32 /nologo" << endl; + if (staticLibTarget) { + t << "LIB32=" << config->var("QMAKE_LIB") << endl; + t << "# ADD LIB32 " << config->var("MSVCDSP_TARGET") << " " << config->var("PRECOMPILED_OBJECT") << endl; + } else { + t << "LINK32=" << config->var("QMAKE_LINK") << endl; + t << "# ADD LINK32 " << config->var("MSVCDSP_LFLAGS") << " " << config->var("MSVCDSP_LIBS") << " " << config->var("MSVCDSP_TARGET") << " " << config->var("PRECOMPILED_OBJECT") << endl; + } + + if (!config->project->values("MSVCDSP_PRE_LINK").isEmpty()) + t << config->project->values("MSVCDSP_PRE_LINK").first(); + + if (!config->project->values("MSVCDSP_POST_LINK").isEmpty()) + t << config->project->values("MSVCDSP_POST_LINK").first(); + + return true; +} + +QString DspMakefileGenerator::writeBuildstepForFileForConfig(const QString &file, const QString &listName, DspMakefileGenerator *config) +{ + QString ret; + QTextStream t(&ret); + + // exclude from build + if (!config->project->values(listName).contains(file)) { + t << "# PROP Exclude_From_Build 1" << endl; + return ret; + } + + if (config->usePCH) { + bool c_file = false; + for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) { + if (file.endsWith(*it)) { + c_file = true; + break; + } + } + if(c_file) { + t << "# SUBTRACT CPP /FI" << config->escapeFilePath(config->namePCH) << " /Yu" << config->escapeFilePath(config->namePCH) << " /Fp" << endl; + return ret; + } else if (config->precompH.endsWith(file)) { + // ### dependency list quickly becomes too long for VS to grok... + t << "USERDEP_" << file << "=" << config->valGlue(config->escapeFilePaths(config->findDependencies(config->precompH)), "", "\t", "") << endl; + t << endl; + t << "# Begin Custom Build - Creating precompiled header from " << file << "..." << endl; + t << "InputPath=.\\" << config->escapeFilePath(file) << endl << endl; + t << config->precompPch + ": $(SOURCE) \"$(IntDir)\" \"$(OUTDIR)\"" << endl; + t << "\t" << config->var("QMAKE_CC") << " /TP /W3 /FD /c /Yc /Fp" << config->precompPch << " /Fo" << config->precompObj << " /Fd\"$(IntDir)\\\\\" " << file << " "; + t << config->var("MSVCDSP_INCPATH") << " " << config->var("MSVCDSP_DEFINES") << " " << config->var("QMAKE_CXXFLAGS") << endl; + t << "# End Custom Build" << endl << endl; + return ret; + } + } + + QString fileBase = QFileInfo(file).completeBaseName(); + + bool hasBuiltin = config->hasBuiltinCompiler(file); + BuildStep allSteps; + + if (!config->swappedBuildSteps.contains(file)) { + QStringList compilers = config->project->values("QMAKE_EXTRA_COMPILERS"); + for (int i = 0; i < compilers.count(); ++i) { + QString compiler = compilers.at(i); + if (config->project->values(compiler + ".input").isEmpty()) + continue; + QString input = config->project->values(compiler + ".input").first(); + QStringList inputList = config->project->values(input); + if (!inputList.contains(file)) + continue; + + QStringList compilerCommands = config->project->values(compiler + ".commands"); + QStringList compilerOutput = config->project->values(compiler + ".output"); + if (compilerCommands.isEmpty() || compilerOutput.isEmpty()) + continue; + + QStringList compilerName = config->project->values(compiler + ".name"); + if (compilerName.isEmpty()) + compilerName << compiler; + QStringList compilerDepends = config->project->values(compiler + ".depends"); + QString compilerDependsCommand = config->project->values(compiler + ".depend_command").join(" "); + if (!compilerDependsCommand.isEmpty()) { + if(!config->canExecute(compilerDependsCommand)) + compilerDependsCommand = QString(); + } + QStringList compilerConfig = config->project->values(compiler + ".CONFIG"); + + if (!config->verifyExtraCompiler(compiler, file)) + continue; + + bool combineAll = compilerConfig.contains("combine"); + if (combineAll && inputList.first() != file) + continue; + + QString fileIn("$(InputPath)"); + + if (combineAll && !inputList.isEmpty()) { + fileIn = inputList.join(" "); + compilerDepends += inputList; + } + + QString fileOut = compilerOutput.first(); + QString fileOutBase = QFileInfo(fileOut).completeBaseName(); + fileOut.replace("${QMAKE_FILE_IN}", fileIn); + fileOut.replace("${QMAKE_FILE_BASE}", fileBase); + fileOut.replace("${QMAKE_FILE_OUT_BASE}", fileOutBase); + fileOut.replace('/', '\\'); + + BuildStep step; + for (int i2 = 0; i2 < compilerDepends.count(); ++i2) { + QString dependency = compilerDepends.at(i2); + dependency.replace("${QMAKE_FILE_IN}", fileIn); + dependency.replace("${QMAKE_FILE_BASE}", fileBase); + dependency.replace("${QMAKE_FILE_OUT_BASE}", fileOutBase); + dependency.replace('/', '\\'); + if (!step.deps.contains(dependency, Qt::CaseInsensitive)) + step.deps << dependency; + } + // depends command + if (!compilerDependsCommand.isEmpty() && config->doDepends()) { + char buff[256]; + QString dep_cmd = config->replaceExtraCompilerVariables(compilerDependsCommand, file, + fileOut); + dep_cmd = Option::fixPathToLocalOS(dep_cmd, true, false); + if(config->canExecute(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()) + step.deps += config->fileFixify(indeps.replace('\n', ' ').simplified().split(' ')); + } + } + } + + + QString mappedFile; + if (hasBuiltin) { + mappedFile = fileOut; + fileOut = fileIn; + fileIn = file; + } + + step.buildStep += " \\\n\t"; + QString command(compilerCommands.join(" ")); + // Replace any newlines with proper line-continuance + command.replace("\n", " \\\n\t"); + // Might be a macro, and not a valid filename, so the replaceExtraCompilerVariables() would eat it + command.replace("${QMAKE_FILE_IN}", config->escapeFilePath(fileIn)); + command.replace("${QMAKE_FILE_BASE}", config->escapeFilePath(fileBase)); + command.replace("${QMAKE_FILE_OUT_BASE}", config->escapeFilePath(fileOutBase)); + command.replace("${QMAKE_FILE_OUT}", config->escapeFilePath(fileOut)); + + command = config->replaceExtraCompilerVariables(command, fileIn, fileOut); + + step.buildName = compilerName.first(); + step.buildStep += command; + step.buildOutputs += fileOut; + + if (hasBuiltin) { + step.deps << fileIn; + config->swappedBuildSteps[mappedFile] = step; + } else { + allSteps << step; + } + } + } else { + allSteps << config->swappedBuildSteps.value(file); + } + + if (allSteps.buildStep.isEmpty()) + return ret; + + int i; + QStringList dependencyList; + // remove dependencies that are also output + for (i = 0; i < 1; ++i) { + QStringList buildOutput(allSteps.buildOutputs.at(i)); + + for (int i2 = 0; i2 < allSteps.deps.count(); ++i2) { + QString dependency = allSteps.deps.at(i2); + if (!buildOutput.contains(dependency) && !dependencyList.contains(dependency)) + dependencyList << dependency; + } + } + QString allDependencies = config->valGlue(dependencyList, "", "\t", ""); + t << "USERDEP_" << file << "=" << allDependencies << endl; + t << "# PROP Ignore_Default_Tool 1" << endl; + t << "# Begin Custom Build - Running " << allSteps.buildName << " on " << file << endl; + t << "InputPath=" << file << endl; + t << "BuildCmds= " << allSteps.buildStep << endl; + for (i = 0; i < allSteps.buildOutputs.count(); ++i) { + t << config->escapeFilePath(allSteps.buildOutputs.at(i)) + << " : $(SOURCE) $(INTDIR) $(OUTDIR)\n\t$(BuildCmds)\n"; + } + t << endl; + t << "# End Custom Build" << endl; + + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_dsp.h b/qmake/generators/win32/msvc_dsp.h new file mode 100644 index 0000000..a870976 --- /dev/null +++ b/qmake/generators/win32/msvc_dsp.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_DSP_H +#define MSVC_DSP_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class FolderGroup; + +class DspMakefileGenerator : public Win32MakefileGenerator +{ + bool init_flag; + bool writeDspHeader(QTextStream &); + bool writeDspParts(QTextStream &); + bool writeFileGroup(QTextStream &t, const QStringList &listNames, const QString &group, const QString &filter); + void writeSubFileGroup(QTextStream &t, FolderGroup *folder); + bool writeBuildstepForFile(QTextStream &t, const QString &file, const QString &listName); + static bool writeDspConfig(QTextStream &t, DspMakefileGenerator *config); + static QString writeBuildstepForFileForConfig(const QString &file, const QString &listName, DspMakefileGenerator *config); + QString configName(DspMakefileGenerator * config); + + bool writeMakefile(QTextStream &); + bool writeProjectMakefile(); + void writeSubDirs(QTextStream &t); + void init(); + +public: + DspMakefileGenerator(); + ~DspMakefileGenerator(); + + bool openOutput(QFile &file, const QString &build) const; + bool hasBuiltinCompiler(const QString &filename) const; + +protected: + virtual bool doDepends() const { return false; } //never necesary + virtual void processSources() { filterIncludedFiles("SOURCES"); filterIncludedFiles("GENERATED_SOURCES"); } + virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &); + inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out) + { return MakefileGenerator::replaceExtraCompilerVariables(val, in, out); } + virtual bool supportsMetaBuild() { return true; } + virtual bool supportsMergedBuilds() { return true; } + virtual bool mergeBuildProject(MakefileGenerator *other); + virtual void processPrlVariable(const QString &, const QStringList &); + virtual bool findLibraries(); + + bool usePCH; + QString precompH, namePCH, + precompObj, precompPch; + + QString platform; + + struct BuildStep { + BuildStep() {} + BuildStep &operator<<(const BuildStep &other) { + deps << other.deps; + buildStep += other.buildStep; + buildName += other.buildName; + buildOutputs += other.buildOutputs; + return *this; + } + + QStringList deps; + QString buildStep; + QString buildName; + QStringList buildOutputs; + }; + QMap<QString, BuildStep> swappedBuildSteps; + + // Holds all configurations for glue (merged) project + QList<DspMakefileGenerator*> mergedProjects; +}; + +inline DspMakefileGenerator::~DspMakefileGenerator() +{ } + +inline bool DspMakefileGenerator::findLibraries() +{ return Win32MakefileGenerator::findLibraries("MSVCDSP_LIBS"); } + +QT_END_NAMESPACE + +#endif // MSVC_DSP_H diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp new file mode 100644 index 0000000..4b1b66d --- /dev/null +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_nmake.h" +#include "option.h" +#include <qregexp.h> +#include <qhash.h> +#include <qdir.h> +#include <time.h> + +QT_BEGIN_NAMESPACE + +NmakeMakefileGenerator::NmakeMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + +} + +bool +NmakeMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "all first clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + writeMakeQmake(t); + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { +#if 0 + if(Option::mkfile::do_stub_makefile) + return MakefileGenerator::writeStubMakefile(t); +#endif + writeNmakeParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; +} + +QStringList &NmakeMakefileGenerator::findDependencies(const QString &file) +{ + QStringList &aList = MakefileGenerator::findDependencies(file); + // Note: The QMAKE_IMAGE_COLLECTION file have all images + // as dependency, so don't add precompiled header then + if (file == project->first("QMAKE_IMAGE_COLLECTION")) + return aList; + for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) { + if(file.endsWith(*it)) { + if(!precompObj.isEmpty() && !aList.contains(precompObj)) + aList += precompObj; + break; + } + } + return aList; +} + +void NmakeMakefileGenerator::writeNmakeParts(QTextStream &t) +{ + writeStandardParts(t); + + // precompiled header + if(usePCH) { + QString precompRule = QString("-c -Yc -Fp%1 -Fo%2").arg(precompPch).arg(precompObj); + t << precompObj << ": " << precompH << " " << findDependencies(precompH).join(" \\\n\t\t") + << "\n\t" << "$(CXX) " + precompRule +" $(CXXFLAGS) $(INCPATH) -TP " << precompH << endl << endl; + } +} + +QString NmakeMakefileGenerator::var(const QString &value) +{ + if (usePCH) { + if ((value == "QMAKE_RUN_CXX_IMP_BATCH" + || value == "QMAKE_RUN_CXX_IMP" + || value == "QMAKE_RUN_CXX")) { + QFileInfo precompHInfo(fileInfo(precompH)); + QString precompRule = QString("-c -FI%1 -Yu%2 -Fp%3") + .arg(precompHInfo.fileName()) + .arg(precompHInfo.fileName()) + .arg(precompPch); + QString p = MakefileGenerator::var(value); + p.replace("-c", precompRule); + // Cannot use -Gm with -FI & -Yu, as this gives an + // internal compiler error, on the newer compilers + p.remove("-Gm"); + return p; + } else if (value == "QMAKE_CXXFLAGS") { + // Remove internal compiler error option + return MakefileGenerator::var(value).remove("-Gm"); + } + } + + // Normal val + return MakefileGenerator::var(value); +} + +void NmakeMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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->values("MAKEFILE").isEmpty()) + project->values("MAKEFILE").append("Makefile"); + if(project->values("QMAKE_QMAKE").isEmpty()) + project->values("QMAKE_QMAKE").append("qmake"); + if(project->isEmpty("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + return; + } + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + processVars(); + + if (!project->values("RES_FILE").isEmpty()) { + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("RES_FILE")); + } + + if(!project->values("DEF_FILE").isEmpty()) + project->values("QMAKE_LFLAGS").append(QString("/DEF:") + escapeFilePath(project->first("DEF_FILE"))); + + if(!project->values("VERSION").isEmpty()) { + QString version = project->values("VERSION")[0]; + int firstDot = version.indexOf("."); + QString major = version.left(firstDot); + QString minor = version.right(version.length() - firstDot - 1); + minor.replace(".", ""); + project->values("QMAKE_LFLAGS").append("/VERSION:" + major + "." + minor); + } + + // Base class init! + MakefileGenerator::init(); + + // Setup PCH variables + precompH = project->first("PRECOMPILED_HEADER"); + usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); + if (usePCH) { + // Created files + precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext; + precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch"; + // Add linking of precompObj (required for whole precompiled classes) + project->values("OBJECTS") += precompObj; + // Add pch file to cleanup + project->values("QMAKE_CLEAN") += precompPch; + // Return to variable pool + project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); + project->values("PRECOMPILED_PCH") = QStringList(precompPch); + } + + QString version = project->first("TARGET_VERSION_EXT"); + if(project->isActiveConfig("shared")) { + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".exp"); + } + if(project->isActiveConfig("debug")) { + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".pdb"); + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".ilk"); + project->values("QMAKE_CLEAN").append("vc*.pdb"); + project->values("QMAKE_CLEAN").append("vc*.idb"); + } +} + +void NmakeMakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + for (int i = 0; i < libDirs.size(); ++i) + libDirs[i].remove("\""); + t << valGlue(libDirs,"/LIBPATH:\"","\" /LIBPATH:\"","\"") << " "; +} + +void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t) +{ + t << ".SUFFIXES:"; + 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; + + if(!project->isActiveConfig("no_batch")) { + // Batchmode doesn't use the non implicit rules QMAKE_RUN_CXX & QMAKE_RUN_CC + project->variables().remove("QMAKE_RUN_CXX"); + project->variables().remove("QMAKE_RUN_CC"); + + QHash<QString, void*> source_directories; + source_directories.insertMulti(".", (void*)1); + QString directories[] = { QString("UI_SOURCES_DIR"), QString("UI_DIR"), QString() }; + for(int y = 0; !directories[y].isNull(); y++) { + QString dirTemp = project->first(directories[y]); + if (dirTemp.endsWith("\\")) + dirTemp.truncate(dirTemp.length()-1); + if(!dirTemp.isEmpty()) + source_directories.insertMulti(dirTemp, (void*)1); + } + QString srcs[] = { QString("SOURCES"), QString("GENERATED_SOURCES"), QString() }; + for(int x = 0; !srcs[x].isNull(); x++) { + QStringList &l = project->values(srcs[x]); + for(QStringList::Iterator sit = l.begin(); sit != l.end(); ++sit) { + QString sep = "\\"; + if((*sit).indexOf(sep) == -1) + sep = "/"; + QString dir = (*sit).section(sep, 0, -2); + if(!dir.isEmpty() && !source_directories[dir]) + source_directories.insertMulti(dir, (void*)1); + } + } + + for(QHash<QString, void*>::Iterator it(source_directories.begin()); it != source_directories.end(); ++it) { + if(it.key().isEmpty()) + continue; + QString objDir = var("OBJECTS_DIR"); + if (objDir == ".\\") + objDir = ""; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) + t << "{" << it.key() << "}" << (*cppit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" + << var("QMAKE_RUN_CXX_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << "{" << it.key() << "}" << (*cit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" + << var("QMAKE_RUN_CC_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; + } + } else { + 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; + } + +} + +void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << fileFixify(Option::output.fileName()) << " " << varGlue("ALL_DEPS"," "," "," ") << "$(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <<var("QMAKE_PRE_LINK"); + if(project->isActiveConfig("staticlib")) { + t << "\n\t" << "$(LIBAPP) $(LIBFLAGS) /OUT:$(DESTDIR_TARGET) @<<" << "\n\t " + << "$(OBJECTS)"; + } else { + t << "\n\t" << "$(LINK) $(LFLAGS) /OUT:$(DESTDIR_TARGET) @<< " << "\n\t " + << "$(OBJECTS) $(LIBS)"; + } + t << endl << "<<"; + QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); + bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && + !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); + if(useSignature) { + t << "\n\tsigntool sign /F " << signature << " $(DESTDIR_TARGET)"; + } + if(!project->isEmpty("QMAKE_POST_LINK")) { + if (useSignature) + t << " && " << var("QMAKE_POST_LINK"); + else + t << "\n\t" << var("QMAKE_POST_LINK"); + } + t << endl; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_nmake.h b/qmake/generators/win32/msvc_nmake.h new file mode 100644 index 0000000..f4e8de6 --- /dev/null +++ b/qmake/generators/win32/msvc_nmake.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_NMAKE_H +#define MSVC_NMAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class NmakeMakefileGenerator : public Win32MakefileGenerator +{ + bool init_flag; + void writeNmakeParts(QTextStream &); + void writeLibDirPart(QTextStream &t); + bool writeMakefile(QTextStream &); + void writeImplicitRulesPart(QTextStream &t); + void writeBuildRulesPart(QTextStream &t); + void init(); + +protected: + virtual QStringList &findDependencies(const QString &file); + QString var(const QString &value); + QString precompH, precompObj, precompPch; + bool usePCH; + +public: + NmakeMakefileGenerator(); + ~NmakeMakefileGenerator(); + +}; + +inline NmakeMakefileGenerator::~NmakeMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MSVC_NMAKE_H diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp new file mode 100644 index 0000000..c1f5ce1 --- /dev/null +++ b/qmake/generators/win32/msvc_objectmodel.cpp @@ -0,0 +1,2636 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_objectmodel.h" +#include "msvc_vcproj.h" +#include <qstringlist.h> +#include <qfileinfo.h> + +QT_BEGIN_NAMESPACE + +// XML Tags --------------------------------------------------------- +const char _Configuration[] = "Configuration"; +const char _Configurations[] = "Configurations"; +const char _File[] = "File"; +const char _FileConfiguration[] = "FileConfiguration"; +const char _Files[] = "Files"; +const char _Filter[] = "Filter"; +const char _Globals[] = "Globals"; +const char _Platform[] = "Platform"; +const char _Platforms[] = "Platforms"; +const char _Tool[] = "Tool"; +const char _VisualStudioProject[] = "VisualStudioProject"; + +// XML Properties --------------------------------------------------- +const char _AddModuleNamesToAssembly[] = "AddModuleNamesToAssembly"; +const char _AdditionalDependencies[] = "AdditionalDependencies"; +const char _AdditionalFiles[] = "AdditionalFiles"; +const char _AdditionalIncludeDirectories[] = "AdditionalIncludeDirectories"; +const char _AdditionalLibraryDirectories[] = "AdditionalLibraryDirectories"; +const char _AdditionalOptions[] = "AdditionalOptions"; +const char _AdditionalUsingDirectories[] = "AdditionalUsingDirectories"; +const char _AssemblerListingLocation[] = "AssemblerListingLocation"; +const char _AssemblerOutput[] = "AssemblerOutput"; +const char _ATLMinimizesCRunTimeLibraryUsage[] = "ATLMinimizesCRunTimeLibraryUsage"; +const char _BaseAddress[] = "BaseAddress"; +const char _BasicRuntimeChecks[] = "BasicRuntimeChecks"; +const char _BrowseInformation[] = "BrowseInformation"; +const char _BrowseInformationFile[] = "BrowseInformationFile"; +const char _BufferSecurityCheck[] = "BufferSecurityCheck"; +const char _BuildBrowserInformation[] = "BuildBrowserInformation"; +const char _CPreprocessOptions[] = "CPreprocessOptions"; +const char _CallingConvention[] = "CallingConvention"; +const char _CharacterSet[] = "CharacterSet"; +const char _CommandLine[] = "CommandLine"; +const char _CompileAs[] = "CompileAs"; +const char _CompileAsManaged[] = "CompileAsManaged"; +const char _CompileOnly[] = "CompileOnly"; +const char _ConfigurationType[] = "ConfigurationType"; +const char _Culture[] = "Culture"; +const char _DLLDataFileName[] = "DLLDataFileName"; +const char _DebugInformationFormat[] = "DebugInformationFormat"; +const char _DefaultCharIsUnsigned[] = "DefaultCharIsUnsigned"; +const char _DefaultCharType[] = "DefaultCharType"; +const char _DelayLoadDLLs[] = "DelayLoadDLLs"; +const char _DeleteExtensionsOnClean[] = "DeleteExtensionsOnClean"; +const char _Description[] = "Description"; +const char _Detect64BitPortabilityProblems[] = "Detect64BitPortabilityProblems"; +const char _DisableLanguageExtensions[] = "DisableLanguageExtensions"; +const char _DisableSpecificWarnings[] = "DisableSpecificWarnings"; +const char _EnableCOMDATFolding[] = "EnableCOMDATFolding"; +const char _EnableErrorChecks[] = "EnableErrorChecks"; +const char _EnableEnhancedInstructionSet[] = "EnableEnhancedInstructionSet"; +const char _EnableFiberSafeOptimizations[] = "EnableFiberSafeOptimizations"; +const char _EnableFunctionLevelLinking[] = "EnableFunctionLevelLinking"; +const char _EnableIntrinsicFunctions[] = "EnableIntrinsicFunctions"; +const char _EntryPointSymbol[] = "EntryPointSymbol"; +const char _ErrorCheckAllocations[] = "ErrorCheckAllocations"; +const char _ErrorCheckBounds[] = "ErrorCheckBounds"; +const char _ErrorCheckEnumRange[] = "ErrorCheckEnumRange"; +const char _ErrorCheckRefPointers[] = "ErrorCheckRefPointers"; +const char _ErrorCheckStubData[] = "ErrorCheckStubData"; +const char _ExceptionHandling[] = "ExceptionHandling"; +const char _ExcludedFromBuild[] = "ExcludedFromBuild"; +const char _ExpandAttributedSource[] = "ExpandAttributedSource"; +const char _ExportNamedFunctions[] = "ExportNamedFunctions"; +const char _FavorSizeOrSpeed[] = "FavorSizeOrSpeed"; +const char _FloatingPointModel[] = "FloatingPointModel"; +const char _FloatingPointExceptions[] = "FloatingPointExceptions"; +const char _ForceConformanceInForLoopScope[] = "ForceConformanceInForLoopScope"; +const char _ForceSymbolReferences[] = "ForceSymbolReferences"; +const char _ForcedIncludeFiles[] = "ForcedIncludeFiles"; +const char _ForcedUsingFiles[] = "ForcedUsingFiles"; +const char _FullIncludePath[] = "FullIncludePath"; +const char _FunctionOrder[] = "FunctionOrder"; +const char _GenerateDebugInformation[] = "GenerateDebugInformation"; +const char _GenerateMapFile[] = "GenerateMapFile"; +const char _GeneratePreprocessedFile[] = "GeneratePreprocessedFile"; +const char _GenerateStublessProxies[] = "GenerateStublessProxies"; +const char _GenerateTypeLibrary[] = "GenerateTypeLibrary"; +const char _GlobalOptimizations[] = "GlobalOptimizations"; +const char _HeaderFileName[] = "HeaderFileName"; +const char _HeapCommitSize[] = "HeapCommitSize"; +const char _HeapReserveSize[] = "HeapReserveSize"; +const char _IgnoreAllDefaultLibraries[] = "IgnoreAllDefaultLibraries"; +const char _IgnoreDefaultLibraryNames[] = "IgnoreDefaultLibraryNames"; +const char _IgnoreEmbeddedIDL[] = "IgnoreEmbeddedIDL"; +const char _IgnoreImportLibrary[] = "IgnoreImportLibrary"; +const char _IgnoreStandardIncludePath[] = "IgnoreStandardIncludePath"; +const char _ImportLibrary[] = "ImportLibrary"; +const char _ImproveFloatingPointConsistency[] = "ImproveFloatingPointConsistency"; +const char _InlineFunctionExpansion[] = "InlineFunctionExpansion"; +const char _InterfaceIdentifierFileName[] = "InterfaceIdentifierFileName"; +const char _IntermediateDirectory[] = "IntermediateDirectory"; +const char _KeepComments[] = "KeepComments"; +const char _LargeAddressAware[] = "LargeAddressAware"; +const char _LinkDLL[] = "LinkDLL"; +const char _LinkIncremental[] = "LinkIncremental"; +const char _LinkTimeCodeGeneration[] = "LinkTimeCodeGeneration"; +const char _LinkToManagedResourceFile[] = "LinkToManagedResourceFile"; +const char _MapExports[] = "MapExports"; +const char _MapFileName[] = "MapFileName"; +const char _MapLines[] = "MapLines "; +const char _MergeSections[] = "MergeSections"; +const char _MergedIDLBaseFileName[] = "MergedIDLBaseFileName"; +const char _MidlCommandFile[] = "MidlCommandFile"; +const char _MinimalRebuild[] = "MinimalRebuild"; +const char _MkTypLibCompatible[] = "MkTypLibCompatible"; +const char _ModuleDefinitionFile[] = "ModuleDefinitionFile"; +const char _Name[] = "Name"; +const char _ObjectFile[] = "ObjectFile"; +const char _OmitFramePointers[] = "OmitFramePointers"; +const char _OpenMP[] = "OpenMP"; +const char _Optimization[] = "Optimization "; +const char _OptimizeForProcessor[] = "OptimizeForProcessor"; +const char _OptimizeForWindows98[] = "OptimizeForWindows98"; +const char _OptimizeForWindowsApplication[] = "OptimizeForWindowsApplication"; +const char _OptimizeReferences[] = "OptimizeReferences"; +const char _OutputDirectory[] = "OutputDirectory"; +const char _OutputFile[] = "OutputFile"; +const char _Outputs[] = "Outputs"; +const char _ParseFiles[] = "ParseFiles"; +const char _PrecompiledHeaderFile[] = "PrecompiledHeaderFile"; +const char _PrecompiledHeaderThrough[] = "PrecompiledHeaderThrough"; +const char _PreprocessorDefinitions[] = "PreprocessorDefinitions"; +const char _PrimaryOutput[] = "PrimaryOutput"; +const char _ProjectGUID[] = "ProjectGUID"; +const char _Keyword[] = "Keyword"; +const char _ProjectType[] = "ProjectType"; +const char _ProgramDatabase[] = "ProgramDatabase"; +const char _ProgramDataBaseFileName[] = "ProgramDataBaseFileName"; +const char _ProgramDatabaseFile[] = "ProgramDatabaseFile"; +const char _ProxyFileName[] = "ProxyFileName"; +const char _RedirectOutputAndErrors[] = "RedirectOutputAndErrors"; +const char _RegisterOutput[] = "RegisterOutput"; +const char _RelativePath[] = "RelativePath"; +const char _RemoteDirectory[] = "RemoteDirectory"; +const char _ResourceOnlyDLL[] = "ResourceOnlyDLL"; +const char _ResourceOutputFileName[] = "ResourceOutputFileName"; +const char _RuntimeLibrary[] = "RuntimeLibrary"; +const char _RuntimeTypeInfo[] = "RuntimeTypeInfo"; +const char _SccProjectName[] = "SccProjectName"; +const char _SccLocalPath[] = "SccLocalPath"; +const char _SetChecksum[] = "SetChecksum"; +const char _ShowIncludes[] = "ShowIncludes"; +const char _ShowProgress[] = "ShowProgress"; +const char _SmallerTypeCheck[] = "SmallerTypeCheck"; +const char _StackCommitSize[] = "StackCommitSize"; +const char _StackReserveSize[] = "StackReserveSize"; +const char _StringPooling[] = "StringPooling"; +const char _StripPrivateSymbols[] = "StripPrivateSymbols"; +const char _StructMemberAlignment[] = "StructMemberAlignment"; +const char _SubSystem[] = "SubSystem"; +const char _SupportUnloadOfDelayLoadedDLL[] = "SupportUnloadOfDelayLoadedDLL"; +const char _SuppressStartupBanner[] = "SuppressStartupBanner"; +const char _SwapRunFromCD[] = "SwapRunFromCD"; +const char _SwapRunFromNet[] = "SwapRunFromNet"; +const char _TargetEnvironment[] = "TargetEnvironment"; +const char _TargetMachine[] = "TargetMachine"; +const char _TerminalServerAware[] = "TerminalServerAware"; +const char _Path[] = "Path"; +const char _TreatWChar_tAsBuiltInType[] = "TreatWChar_tAsBuiltInType"; +const char _TurnOffAssemblyGeneration[] = "TurnOffAssemblyGeneration"; +const char _TypeLibraryFile[] = "TypeLibraryFile"; +const char _TypeLibraryName[] = "TypeLibraryName"; +const char _TypeLibraryResourceID[] = "TypeLibraryResourceID"; +const char _UndefineAllPreprocessorDefinitions[]= "UndefineAllPreprocessorDefinitions"; +const char _UndefinePreprocessorDefinitions[] = "UndefinePreprocessorDefinitions"; +const char _UniqueIdentifier[] = "UniqueIdentifier"; +const char _UseOfATL[] = "UseOfATL"; +const char _UseOfMfc[] = "UseOfMfc"; +const char _UsePrecompiledHeader[] = "UsePrecompiledHeader"; +const char _ValidateParameters[] = "ValidateParameters"; +const char _VCCLCompilerTool[] = "VCCLCompilerTool"; +const char _VCLibrarianTool[] = "VCLibrarianTool"; +const char _VCLinkerTool[] = "VCLinkerTool"; +const char _VCCustomBuildTool[] = "VCCustomBuildTool"; +const char _VCResourceCompilerTool[] = "VCResourceCompilerTool"; +const char _VCMIDLTool[] = "VCMIDLTool"; +const char _Version[] = "Version"; +const char _WarnAsError[] = "WarnAsError"; +const char _WarningLevel[] = "WarningLevel"; +const char _WholeProgramOptimization[] = "WholeProgramOptimization"; +const char _CompileForArchitecture[] = "CompileForArchitecture"; +const char _InterworkCalls[] = "InterworkCalls"; + +// XmlOutput stream functions ------------------------------ +inline XmlOutput::xml_output attrT(const char *name, const triState v) +{ + if(v == unset) + return noxml(); + return attr(name, (v == _True ? "true" : "false")); +} + +inline XmlOutput::xml_output attrE(const char *name, int v) +{ + return attr(name, QString::number(v)); +} + +/*ifNot version*/ +inline XmlOutput::xml_output attrE(const char *name, int v, int ifn) +{ + if (v == ifn) + return noxml(); + return attr(name, QString::number(v)); +} + +inline XmlOutput::xml_output attrL(const char *name, qint64 v) +{ + return attr(name, QString::number(v)); +} + +/*ifNot version*/ +inline XmlOutput::xml_output attrL(const char *name, qint64 v, qint64 ifn) +{ + if (v == ifn) + return noxml(); + return attr(name, QString::number(v)); +} + +inline XmlOutput::xml_output attrS(const char *name, const QString &v) +{ + if(v.isEmpty()) + return noxml(); + return attr(name, v); +} + +inline XmlOutput::xml_output attrX(const char *name, const QStringList &v, const char *s = ",") +{ + if(v.isEmpty()) + return noxml(); + return attr(name, v.join(s)); +} + +// VCCLCompilerTool ------------------------------------------------- +VCCLCompilerTool::VCCLCompilerTool() + : AssemblerOutput(asmListingNone), + BasicRuntimeChecks(runtimeBasicCheckNone), + BrowseInformation(brInfoNone), + BufferSecurityCheck(_False), + CallingConvention(callConventionDefault), + CompileAs(compileAsDefault), + CompileAsManaged(managedDefault), + CompileOnly(unset), + DebugInformationFormat(debugDisabled), + DefaultCharIsUnsigned(unset), + Detect64BitPortabilityProblems(unset), + DisableLanguageExtensions(unset), + EnableEnhancedInstructionSet(archNotSet), + EnableFiberSafeOptimizations(unset), + EnableFunctionLevelLinking(unset), + EnableIntrinsicFunctions(unset), + ExceptionHandling(ehDefault), + ExpandAttributedSource(unset), + FavorSizeOrSpeed(favorNone), + FloatingPointModel(floatingPointNotSet), + FloatingPointExceptions(unset), + ForceConformanceInForLoopScope(unset), + GeneratePreprocessedFile(preprocessNo), + GlobalOptimizations(unset), + IgnoreStandardIncludePath(unset), + ImproveFloatingPointConsistency(unset), + InlineFunctionExpansion(expandDefault), + KeepComments(unset), + MinimalRebuild(unset), + OmitFramePointers(unset), + OpenMP(unset), + Optimization(optimizeCustom), + OptimizeForProcessor(procOptimizeBlended), + OptimizeForWindowsApplication(unset), + ProgramDataBaseFileName(""), + RuntimeLibrary(rtMultiThreaded), + RuntimeTypeInfo(unset), + ShowIncludes(unset), + SmallerTypeCheck(unset), + StringPooling(unset), + StructMemberAlignment(alignNotSet), + SuppressStartupBanner(unset), + TreatWChar_tAsBuiltInType(unset), + TurnOffAssemblyGeneration(unset), + UndefineAllPreprocessorDefinitions(unset), + UsePrecompiledHeader(pchNone), + WarnAsError(unset), + WarningLevel(warningLevel_0), + WholeProgramOptimization(unset), + CompileForArchitecture(archUnknown), + InterworkCalls(unset) +{ +} + +/* + * Some values for the attribute UsePrecompiledHeader have changed from VS 2003 to VS 2005, + * see the following chart, so we need a function that transforms those values if we are + * using NET2005: + * + * Meaning 2003 2005 + * ----------------------------------------- + * Don't use PCH 0 0 + * Create PCH (/Yc) 1 1 + * Automatically generate (/YX) 2 (seems that it was removed) + * Use specific PCH (/Yu) 3 2 + * + */ +inline XmlOutput::xml_output xformUsePrecompiledHeaderForNET2005(pchOption whatPch, DotNET compilerVersion) +{ + if (compilerVersion >= NET2005) { + if (whatPch == pchGenerateAuto) whatPch = (pchOption)0; + if (whatPch == pchUseUsingSpecific) whatPch = (pchOption)2; + } + return attrE(_UsePrecompiledHeader, whatPch); +} + +inline XmlOutput::xml_output xformExceptionHandlingNET2005(exceptionHandling eh, DotNET compilerVersion) +{ + if (eh == ehDefault) + return noxml(); + + if (compilerVersion >= NET2005) + return attrE(_ExceptionHandling, eh); + + return attrS(_ExceptionHandling, (eh == ehNoSEH ? "true" : "false")); +} + +XmlOutput &operator<<(XmlOutput &xml, const VCCLCompilerTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, _VCCLCompilerTool) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_AdditionalUsingDirectories, tool.AdditionalUsingDirectories) + << attrS(_AssemblerListingLocation, tool.AssemblerListingLocation) + << attrE(_AssemblerOutput, tool.AssemblerOutput, /*ifNot*/ asmListingNone) + << attrE(_BasicRuntimeChecks, tool.BasicRuntimeChecks, /*ifNot*/ runtimeBasicCheckNone) + << attrE(_BrowseInformation, tool.BrowseInformation, /*ifNot*/ brInfoNone) + << attrS(_BrowseInformationFile, tool.BrowseInformationFile) + << attrT(_BufferSecurityCheck, tool.BufferSecurityCheck) + << attrE(_CallingConvention, tool.CallingConvention, /*ifNot*/ callConventionDefault) + << attrE(_CompileAs, tool.CompileAs, compileAsDefault) + << attrE(_CompileAsManaged, tool.CompileAsManaged, /*ifNot*/ managedDefault) + << attrT(_CompileOnly, tool.CompileOnly) + << attrE(_DebugInformationFormat, tool.DebugInformationFormat, /*ifNot*/ debugUnknown) + << attrT(_DefaultCharIsUnsigned, tool.DefaultCharIsUnsigned) + << attrT(_Detect64BitPortabilityProblems, tool.Detect64BitPortabilityProblems) + << attrT(_DisableLanguageExtensions, tool.DisableLanguageExtensions) + << attrX(_DisableSpecificWarnings, tool.DisableSpecificWarnings) + << attrE(_EnableEnhancedInstructionSet, tool.EnableEnhancedInstructionSet, /*ifnot*/ archNotSet) + << attrT(_EnableFiberSafeOptimizations, tool.EnableFiberSafeOptimizations) + << attrT(_EnableFunctionLevelLinking, tool.EnableFunctionLevelLinking) + << attrT(_EnableIntrinsicFunctions, tool.EnableIntrinsicFunctions) + << xformExceptionHandlingNET2005(tool.ExceptionHandling, tool.config->CompilerVersion) + << attrT(_ExpandAttributedSource, tool.ExpandAttributedSource) + << attrE(_FavorSizeOrSpeed, tool.FavorSizeOrSpeed, /*ifNot*/ favorNone) + + << attrE(_FloatingPointModel, tool.FloatingPointModel, /*ifNot*/ floatingPointNotSet) + << attrT(_FloatingPointExceptions, tool.FloatingPointExceptions) + + << attrT(_ForceConformanceInForLoopScope, tool.ForceConformanceInForLoopScope) + << attrX(_ForcedIncludeFiles, tool.ForcedIncludeFiles) + << attrX(_ForcedUsingFiles, tool.ForcedUsingFiles) + << attrE(_GeneratePreprocessedFile, tool.GeneratePreprocessedFile, /*ifNot*/ preprocessUnknown) + << attrT(_GlobalOptimizations, tool.GlobalOptimizations) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrT(_ImproveFloatingPointConsistency, tool.ImproveFloatingPointConsistency) + << attrE(_InlineFunctionExpansion, tool.InlineFunctionExpansion, /*ifNot*/ expandDefault) + << attrT(_KeepComments, tool.KeepComments) + << attrT(_MinimalRebuild, tool.MinimalRebuild) + << attrS(_ObjectFile, tool.ObjectFile) + << attrT(_OmitFramePointers, tool.OmitFramePointers) + << attrT(_OpenMP, tool.OpenMP) + << attrE(_Optimization, tool.Optimization, /*ifNot*/ optimizeDefault) + << attrE(_OptimizeForProcessor, tool.OptimizeForProcessor, /*ifNot*/ procOptimizeBlended) + << attrT(_OptimizeForWindowsApplication, tool.OptimizeForWindowsApplication) + << attrS(_OutputFile, tool.OutputFile) + << attrS(_PrecompiledHeaderFile, tool.PrecompiledHeaderFile) + << attrS(_PrecompiledHeaderThrough, tool.PrecompiledHeaderThrough) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << (tool.ProgramDataBaseFileName.isNull() ? noxml() : attr(_ProgramDataBaseFileName, tool.ProgramDataBaseFileName)) + << attrE(_RuntimeLibrary, tool.RuntimeLibrary, /*ifNot*/ rtUnknown) + << attrT(_RuntimeTypeInfo, tool.RuntimeTypeInfo) + << attrT(_ShowIncludes, tool.ShowIncludes) + << attrT(_SmallerTypeCheck, tool.SmallerTypeCheck) + << attrT(_StringPooling, tool.StringPooling) + << attrE(_StructMemberAlignment, tool.StructMemberAlignment, /*ifNot*/ alignNotSet) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrT(_TreatWChar_tAsBuiltInType, tool.TreatWChar_tAsBuiltInType) + << attrT(_TurnOffAssemblyGeneration, tool.TurnOffAssemblyGeneration) + << attrT(_UndefineAllPreprocessorDefinitions, tool.UndefineAllPreprocessorDefinitions) + << attrX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions) + << (!tool.PrecompiledHeaderFile.isEmpty() || !tool.PrecompiledHeaderThrough.isEmpty() ? xformUsePrecompiledHeaderForNET2005(tool.UsePrecompiledHeader, tool.config->CompilerVersion) : noxml()) + << attrT(_WarnAsError, tool.WarnAsError) + << attrE(_WarningLevel, tool.WarningLevel, /*ifNot*/ warningLevelUnknown) + << attrT(_WholeProgramOptimization, tool.WholeProgramOptimization) + << attrE(_CompileForArchitecture, tool.CompileForArchitecture, /*ifNot*/ archUnknown) + << attrT(_InterworkCalls, tool.InterworkCalls) + + << closetag(_Tool); +} + +bool VCCLCompilerTool::parseOption(const char* option) +{ + // skip index 0 ('/' or '-') + char first = option[1]; + char second = option[2]; + char third = option[3]; + char fourth = option[4]; + bool found = true; + + switch (first) { + case '?': + case 'h': + qWarning("Generator: Option '/?', '/help': MSVC.NET projects do not support outputting help info"); + found = false; + break; + case '@': + qWarning("Generator: Option '/@': MSVC.NET projects do not support the use of a response file"); + found = false; + break; + case 'l': + qWarning("Generator: Option '/link': qmake generator does not support passing link options through the compiler tool"); + found = false; + break; + case 'A': + if(second != 'I') { + found = false; break; + } + AdditionalUsingDirectories += option+3; + break; + case 'C': + KeepComments = _True; + break; + case 'D': + PreprocessorDefinitions += option+2; + break; + case 'E': + if(second == 'H') { + QString opt(option); + if (opt.contains('a') && !opt.contains('s') && !opt.contains('c')) + ExceptionHandling = ehSEH; + else if (!opt.contains('a') && opt.contains("s-") && opt.contains("c-")) + ExceptionHandling = ehNone; + else if (!opt.contains('a') && opt.contains('s') && opt.contains('c')) + ExceptionHandling = ehNoSEH; + else { + // ExceptionHandling must be false, or it will override + // with an /EHsc option + ExceptionHandling = ehNone; + AdditionalOptions += option; + } + if (config->CompilerVersion < NET2005 + && ExceptionHandling == ehSEH) { + ExceptionHandling = ehNone; + AdditionalOptions += option; + } + break; + } + GeneratePreprocessedFile = preprocessYes; + break; + case 'F': + if(second <= '9' && second >= '0') { + AdditionalOptions += option; + break; + } else { + switch (second) { + case 'A': + if(third == 'c') { + AssemblerOutput = asmListingAsmMachine; + if(fourth == 's') + AssemblerOutput = asmListingAsmMachineSrc; + } else if(third == 's') { + AssemblerOutput = asmListingAsmSrc; + } else { + AssemblerOutput = asmListingAssemblyOnly; + } + break; + case 'a': + AssemblerListingLocation = option+3; + break; + case 'I': + ForcedIncludeFiles += option+3; + break; + case 'R': + BrowseInformation = brAllInfo; + BrowseInformationFile = option+3; + break; + case 'r': + BrowseInformation = brNoLocalSymbols; + BrowseInformationFile = option+3; + break; + case 'U': + ForcedUsingFiles += option+3; + break; + case 'd': + ProgramDataBaseFileName = option+3; + break; + case 'e': + OutputFile = option+3; + break; + case 'm': + AdditionalOptions += option; + break; + case 'o': + ObjectFile = option+3; + break; + case 'p': + PrecompiledHeaderFile = option+3; + break; + case 'x': + ExpandAttributedSource = _True; + break; + default: + found = false; break; + } + } + break; + case 'G': + switch (second) { + case '3': + case '4': + qWarning("Option '/G3' and '/G4' were phased out in Visual C++ 5.0"); + found = false; break; + case '5': + OptimizeForProcessor = procOptimizePentium; + break; + case '6': + case 'B': + OptimizeForProcessor = procOptimizePentiumProAndAbove; + break; + case '7': + OptimizeForProcessor = procOptimizePentium4AndAbove; + break; + case 'A': + OptimizeForWindowsApplication = _True; + break; + case 'F': + StringPooling = _True; + break; + case 'H': + AdditionalOptions += option; + break; + case 'L': + WholeProgramOptimization = _True; + if(third == '-') + WholeProgramOptimization = _False; + break; + case 'R': + RuntimeTypeInfo = _True; + if(third == '-') + RuntimeTypeInfo = _False; + break; + case 'S': + BufferSecurityCheck = _True; + break; + case 'T': + EnableFiberSafeOptimizations = _True; + break; + case 'X': + // Same as the /EHsc option, which is Exception Handling without SEH + ExceptionHandling = ehNoSEH; + if (third == '-') + ExceptionHandling = ehNone; + break; + case 'Z': + case 'e': + case 'h': + AdditionalOptions += option; + break; + case 'd': + CallingConvention = callConventionCDecl; + break; + case 'f': + StringPooling = _True; + AdditionalOptions += option; + break; + case 'm': + MinimalRebuild = _True; + if(third == '-') + MinimalRebuild = _False; + break; + case 'r': + CallingConvention = callConventionFastCall; + break; + case 's': + // Warning: following [num] is not used, + // were should we put it? + BufferSecurityCheck = _True; + break; + case 'y': + EnableFunctionLevelLinking = _True; + break; + case 'z': + CallingConvention = callConventionStdCall; + break; + default: + found = false; break; + } + break; + case 'H': + AdditionalOptions += option; + break; + case 'I': + AdditionalIncludeDirectories += option+2; + break; + case 'J': + DefaultCharIsUnsigned = _True; + break; + case 'L': + if(second == 'D') { + AdditionalOptions += option; + break; + } + found = false; break; + case 'M': + if(second == 'D') { + RuntimeLibrary = rtMultiThreadedDLL; + if(third == 'd') + RuntimeLibrary = rtMultiThreadedDebugDLL; + break; + } else if(second == 'L') { + RuntimeLibrary = rtSingleThreaded; + if(third == 'd') + RuntimeLibrary = rtSingleThreadedDebug; + break; + } else if(second == 'T') { + RuntimeLibrary = rtMultiThreaded; + if(third == 'd') + RuntimeLibrary = rtMultiThreadedDebug; + break; + } + found = false; break; + case 'O': + switch (second) { + case '1': + Optimization = optimizeMinSpace; + break; + case '2': + Optimization = optimizeMaxSpeed; + break; + case 'a': + AdditionalOptions += option; + break; + case 'b': + if(third == '0') + InlineFunctionExpansion = expandDisable; + else if(third == '1') + InlineFunctionExpansion = expandOnlyInline; + else if(third == '2') + InlineFunctionExpansion = expandAnySuitable; + else + found = false; + break; + case 'd': + Optimization = optimizeDisabled; + break; + case 'g': + GlobalOptimizations = _True; + break; + case 'i': + EnableIntrinsicFunctions = _True; + break; + case 'p': + ImproveFloatingPointConsistency = _True; + if(third == '-') + ImproveFloatingPointConsistency = _False; + break; + case 's': + FavorSizeOrSpeed = favorSize; + break; + case 't': + FavorSizeOrSpeed = favorSpeed; + break; + case 'w': + AdditionalOptions += option; + break; + case 'x': + Optimization = optimizeFull; + break; + case 'y': + OmitFramePointers = _True; + if(third == '-') + OmitFramePointers = _False; + break; + default: + found = false; break; + } + break; + case 'P': + GeneratePreprocessedFile = preprocessYes; + break; + case 'Q': + if(second == 'I') { + AdditionalOptions += option; + break; + } else if (second == 'R') { + QString opt = option + 3; + if (opt == "interwork-return") { + InterworkCalls = _True; + break; + } else if (opt == "arch4") { + CompileForArchitecture = archArmv4; + break; + } else if (opt == "arch5") { + CompileForArchitecture = archArmv5; + break; + } else if (opt == "arch4T") { + CompileForArchitecture = archArmv4T; + break; + } else if (opt == "arch5T") { + CompileForArchitecture = archArmv5T; + break; + } + } else if (second == 'M') { + QString opt = option + 3; + if (opt == "mips1") { + CompileForArchitecture = archMips1; + break; + } + else if (opt == "mips2") { + CompileForArchitecture = archMips2; + break; + } + else if (opt == "mips3") { + CompileForArchitecture = archMips3; + break; + } + else if (opt == "mips4") { + CompileForArchitecture = archMips4; + break; + } + else if (opt == "mips5") { + CompileForArchitecture = archMips5; + break; + } + else if (opt == "mips16") { + CompileForArchitecture = archMips16; + break; + } + else if (opt == "mips32") { + CompileForArchitecture = archMips32; + break; + } + else if (opt == "mips64") { + CompileForArchitecture = archMips64; + break; + } + } + found = false; break; + case 'R': + if(second == 'T' && third == 'C') { + if(fourth == '1') + BasicRuntimeChecks = runtimeBasicCheckAll; + else if(fourth == 'c') + SmallerTypeCheck = _True; + else if(fourth == 's') + BasicRuntimeChecks = runtimeCheckStackFrame; + else if(fourth == 'u') + BasicRuntimeChecks = runtimeCheckUninitVariables; + else + found = false; break; + } + break; + case 'T': + if(second == 'C') { + CompileAs = compileAsC; + } else if(second == 'P') { + CompileAs = compileAsCPlusPlus; + } else { + qWarning("Generator: Options '/Tp<filename>' and '/Tc<filename>' are not supported by qmake"); + found = false; break; + } + break; + case 'U': + UndefinePreprocessorDefinitions += option+2; + break; + case 'V': + AdditionalOptions += option; + break; + case 'W': + switch (second) { + case 'a': + case '4': + WarningLevel = warningLevel_4; + break; + case '3': + WarningLevel = warningLevel_3; + break; + case '2': + WarningLevel = warningLevel_2; + break; + case '1': + WarningLevel = warningLevel_1; + break; + case '0': + WarningLevel = warningLevel_0; + break; + case 'L': + AdditionalOptions += option; + break; + case 'X': + WarnAsError = _True; + break; + case 'p': + if(third == '6' && fourth == '4') { + Detect64BitPortabilityProblems = _True; + break; + } + // Fallthrough + default: + found = false; break; + } + break; + case 'X': + IgnoreStandardIncludePath = _True; + break; + case 'Y': + switch (second) { + case '\0': + case '-': + AdditionalOptions += option; + break; + case 'X': + UsePrecompiledHeader = pchGenerateAuto; + PrecompiledHeaderFile = option+3; + break; + case 'c': + UsePrecompiledHeader = pchCreateUsingSpecific; + PrecompiledHeaderFile = option+3; + break; + case 'd': + case 'l': + AdditionalOptions += option; + break; + case 'u': + UsePrecompiledHeader = pchUseUsingSpecific; + PrecompiledHeaderFile = option+3; + break; + default: + found = false; break; + } + break; + case 'Z': + switch (second) { + case '7': + DebugInformationFormat = debugOldStyleInfo; + break; + case 'I': + DebugInformationFormat = debugEditAndContinue; + break; + case 'd': + DebugInformationFormat = debugLineInfoOnly; + break; + case 'i': + DebugInformationFormat = debugEnabled; + break; + case 'l': + DebugInformationFormat = debugEditAndContinue; + break; + case 'a': + DisableLanguageExtensions = _True; + break; + case 'e': + DisableLanguageExtensions = _False; + break; + case 'c': + if(third == ':') { + const char *c = option + 4; + // Go to the end of the option + while ( *c != '\0' && *c != ' ' && *c != '-') + ++c; + if(fourth == 'f') + ForceConformanceInForLoopScope = ((*c) == '-' ? _False : _True); + else if(fourth == 'w') + TreatWChar_tAsBuiltInType = ((*c) == '-' ? _False : _True); + else + found = false; + } else { + found = false; break; + } + break; + case 'g': + case 'm': + case 's': + AdditionalOptions += option; + break; + case 'p': + switch (third) + { + case '\0': + case '1': + StructMemberAlignment = alignSingleByte; + if(fourth == '6') + StructMemberAlignment = alignSixteenBytes; + break; + case '2': + StructMemberAlignment = alignTwoBytes; + break; + case '4': + StructMemberAlignment = alignFourBytes; + break; + case '8': + StructMemberAlignment = alignEightBytes; + break; + default: + found = false; break; + } + break; + default: + found = false; break; + } + break; + case 'a': + if (second == 'r' && third == 'c' && fourth == 'h') { + if (option[5] == ':') { + const char *o = option; + if (o[6] == 'S' && o[7] == 'S' && o[8] == 'E') { + EnableEnhancedInstructionSet = o[9] == '2' ? archSSE2 : archSSE; + break; + } + } + } + found = false; + break; + case 'b': // see http://msdn2.microsoft.com/en-us/library/ms173499.aspx + if (second == 'i' && third == 'g' && fourth == 'o') { + const char *o = option; + if (o[5] == 'b' && o[6] == 'j') { + AdditionalOptions += option; + break; + } + } + found = false; + break; + case 'c': + if(second == '\0') { + CompileOnly = _True; + } else if(second == 'l') { + if (config->CompilerVersion < NET2005) { + if(*(option+5) == 'n') { + CompileAsManaged = managedAssemblyPure; + TurnOffAssemblyGeneration = _True; + } else if(*(option+5) == 'p') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:pure option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 's') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:safe option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 'o') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:oldSyntax option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 'i') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "initialAppDomain enum value unknown, using /crl"); + } else { + CompileAsManaged = managedAssemblyPure; + } + } else { + if(*(option+5) == 'n') { + CompileAsManaged = managedAssembly; + TurnOffAssemblyGeneration = _True; + } else if(*(option+5) == 'p') { + CompileAsManaged = managedAssemblyPure; + } else if(*(option+5) == 's') { + CompileAsManaged = managedAssemblySafe; + } else if(*(option+5) == 'o') { + CompileAsManaged = managedAssemblyOldSyntax; + } else if(*(option+5) == 'i') { + CompileAsManaged = managedAssembly; + warn_msg(WarnLogic, "initialAppDomain enum value unknown, using /crl default"); + } else { + CompileAsManaged = managedAssembly; + } + } + } else { + found = false; break; + } + break; + case 'd': + if(second != 'r') { + found = false; break; + } + CompileAsManaged = managedAssembly; + break; + case 'f': + if(second == 'p' && third == ':') { + // Go to the end of the option + const char *c = option + 4; + while (*c != '\0' && *c != ' ' && *c != '-') + ++c; + switch (fourth) { + case 'e': + FloatingPointExceptions = ((*c) == '-' ? _False : _True); + break; + case 'f': + FloatingPointModel = floatingPointFast; + break; + case 'p': + FloatingPointModel = floatingPointPrecise; + break; + case 's': + FloatingPointModel = floatingPointStrict; + break; + default: + found = false; + break; + } + } + break; + case 'n': + if(second == 'o' && third == 'B' && fourth == 'o') { + AdditionalOptions += "/noBool"; + break; + } + if(second == 'o' && third == 'l' && fourth == 'o') { + SuppressStartupBanner = _True; + break; + } + found = false; break; + case 'o': + if (second == 'p' && third == 'e' && fourth == 'n') { + OpenMP = _True; + break; + } + found = false; break; + case 's': + if(second == 'h' && third == 'o' && fourth == 'w') { + ShowIncludes = _True; + break; + } + found = false; break; + case 'u': + UndefineAllPreprocessorDefinitions = _True; + break; + case 'v': + if(second == 'd' || second == 'm') { + AdditionalOptions += option; + break; + } + found = false; break; + case 'w': + switch (second) { + case '\0': + WarningLevel = warningLevel_0; + break; + case 'd': + DisableSpecificWarnings += option+3; + break; + default: + AdditionalOptions += option; + } + break; + default: + AdditionalOptions += option; + break; + } + if(!found) { + warn_msg(WarnLogic, "Could not parse Compiler option: %s, added as AdditionalOption", option); + AdditionalOptions += option; + } + return true; +} + +// VCLinkerTool ----------------------------------------------------- +VCLinkerTool::VCLinkerTool() + : EnableCOMDATFolding(optFoldingDefault), + GenerateDebugInformation(unset), + GenerateMapFile(unset), + HeapCommitSize(-1), + HeapReserveSize(-1), + IgnoreAllDefaultLibraries(unset), + IgnoreEmbeddedIDL(unset), + IgnoreImportLibrary(_True), + LargeAddressAware(addrAwareDefault), + LinkDLL(unset), + LinkIncremental(linkIncrementalDefault), + LinkTimeCodeGeneration(optLTCGDefault), + MapExports(unset), + MapLines(unset), + OptimizeForWindows98(optWin98Default), + OptimizeReferences(optReferencesDefault), + RegisterOutput(unset), + ResourceOnlyDLL(unset), + SetChecksum(unset), + ShowProgress(linkProgressNotSet), + StackCommitSize(-1), + StackReserveSize(-1), + SubSystem(subSystemNotSet), + SupportUnloadOfDelayLoadedDLL(unset), + SuppressStartupBanner(unset), + SwapRunFromCD(unset), + SwapRunFromNet(unset), + TargetMachine(machineNotSet), + TerminalServerAware(termSvrAwareDefault), + TurnOffAssemblyGeneration(unset), + TypeLibraryResourceID(0) +{ +} + +XmlOutput &operator<<(XmlOutput &xml, const VCLinkerTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, _VCLinkerTool) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies, " ") + << attrX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_AddModuleNamesToAssembly, tool.AddModuleNamesToAssembly) + << attrS(_BaseAddress, tool.BaseAddress) + << attrX(_DelayLoadDLLs, tool.DelayLoadDLLs) + << attrE(_EnableCOMDATFolding, tool.EnableCOMDATFolding, /*ifNot*/ optFoldingDefault) + << attrS(_EntryPointSymbol, tool.EntryPointSymbol) + << attrX(_ForceSymbolReferences, tool.ForceSymbolReferences) + << attrS(_FunctionOrder, tool.FunctionOrder) + << attrT(_GenerateDebugInformation, tool.GenerateDebugInformation) + << attrT(_GenerateMapFile, tool.GenerateMapFile) + << attrL(_HeapCommitSize, tool.HeapCommitSize, /*ifNot*/ -1) + << attrL(_HeapReserveSize, tool.HeapReserveSize, /*ifNot*/ -1) + << attrT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrX(_IgnoreDefaultLibraryNames, tool.IgnoreDefaultLibraryNames) + << attrT(_IgnoreEmbeddedIDL, tool.IgnoreEmbeddedIDL) + << attrT(_IgnoreImportLibrary, tool.IgnoreImportLibrary) + << attrS(_ImportLibrary, tool.ImportLibrary) + << attrE(_LargeAddressAware, tool.LargeAddressAware, /*ifNot*/ addrAwareDefault) + << attrT(_LinkDLL, tool.LinkDLL) + << attrE(_LinkIncremental, tool.LinkIncremental, /*ifNot*/ linkIncrementalDefault) + << attrE(_LinkTimeCodeGeneration, tool.LinkTimeCodeGeneration) + << attrS(_LinkToManagedResourceFile, tool.LinkToManagedResourceFile) + << attrT(_MapExports, tool.MapExports) + << attrS(_MapFileName, tool.MapFileName) + << attrT(_MapLines, tool.MapLines) + << attrS(_MergedIDLBaseFileName, tool.MergedIDLBaseFileName) + << attrS(_MergeSections, tool.MergeSections) + << attrS(_MidlCommandFile, tool.MidlCommandFile) + << attrS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) + << attrE(_OptimizeForWindows98, tool.OptimizeForWindows98, /*ifNot*/ optWin98Default) + << attrE(_OptimizeReferences, tool.OptimizeReferences, /*ifNot*/ optReferencesDefault) + << attrS(_OutputFile, tool.OutputFile) + << attr(_ProgramDatabaseFile, tool.ProgramDatabaseFile) + << attrT(_RegisterOutput, tool.RegisterOutput) + << attrT(_ResourceOnlyDLL, tool.ResourceOnlyDLL) + << attrT(_SetChecksum, tool.SetChecksum) + << attrE(_ShowProgress, tool.ShowProgress, /*ifNot*/ linkProgressNotSet) + << attrL(_StackCommitSize, tool.StackCommitSize, /*ifNot*/ -1) + << attrL(_StackReserveSize, tool.StackReserveSize, /*ifNot*/ -1) + << attrS(_StripPrivateSymbols, tool.StripPrivateSymbols) + << attrE(_SubSystem, tool.SubSystem) + << attrT(_SupportUnloadOfDelayLoadedDLL, tool.SupportUnloadOfDelayLoadedDLL) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrT(_SwapRunFromCD, tool.SwapRunFromCD) + << attrT(_SwapRunFromNet, tool.SwapRunFromNet) + << attrE(_TargetMachine, tool.TargetMachine, /*ifNot*/ machineNotSet) + << attrE(_TerminalServerAware, tool.TerminalServerAware, /*ifNot*/ termSvrAwareDefault) + << attrT(_TurnOffAssemblyGeneration, tool.TurnOffAssemblyGeneration) + << attrS(_TypeLibraryFile, tool.TypeLibraryFile) + << attrL(_TypeLibraryResourceID, tool.TypeLibraryResourceID, /*ifNot*/ rcUseDefault) + << attrS(_Version, tool.Version) + << closetag(_Tool); +} + +// Hashing routine to do fast option lookups ---- +// Slightly rewritten to stop on ':' ',' and '\0' +// Original routine in qtranslator.cpp ---------- +static uint elfHash(const char* name) +{ + const uchar *k; + uint h = 0; + uint g; + + if(name) { + k = (const uchar *) name; + while((*k) && + (*k)!= ':' && + (*k)!=',' && + (*k)!=' ') { + h = (h << 4) + *k++; + if((g = (h & 0xf0000000)) != 0) + h ^= g >> 24; + h &= ~g; + } + } + if(!h) + h = 1; + return h; +} + +//#define USE_DISPLAY_HASH +#ifdef USE_DISPLAY_HASH +static void displayHash(const char* str) +{ + printf("case 0x%07x: // %s\n break;\n", elfHash(str), str); +} +#endif + +bool VCLinkerTool::parseOption(const char* option) +{ +#ifdef USE_DISPLAY_HASH + // Main options + displayHash("/ALIGN"); displayHash("/ALLOWBIND"); displayHash("/ASSEMBLYMODULE"); + displayHash("/ASSEMBLYRESOURCE"); displayHash("/BASE"); displayHash("/DEBUG"); + displayHash("/DEF"); displayHash("/DEFAULTLIB"); displayHash("/DELAY"); + displayHash("/DELAYLOAD"); displayHash("/DLL"); displayHash("/DRIVER"); + displayHash("/ENTRY"); displayHash("/EXETYPE"); displayHash("/EXPORT"); + displayHash("/FIXED"); displayHash("/FORCE"); displayHash("/HEAP"); + displayHash("/IDLOUT"); displayHash("/IGNORE"); displayHash("/IGNOREIDL"); displayHash("/IMPLIB"); + displayHash("/INCLUDE"); displayHash("/INCREMENTAL"); displayHash("/LARGEADDRESSAWARE"); + displayHash("/LIBPATH"); displayHash("/LTCG"); displayHash("/MACHINE"); + displayHash("/MAP"); displayHash("/MAPINFO"); displayHash("/MERGE"); + displayHash("/MIDL"); displayHash("/NOASSEMBLY"); displayHash("/NODEFAULTLIB"); + displayHash("/NOENTRY"); displayHash("/NOLOGO"); displayHash("/OPT"); + displayHash("/ORDER"); displayHash("/OUT"); displayHash("/PDB"); + displayHash("/PDBSTRIPPED"); displayHash("/RELEASE"); displayHash("/SECTION"); + displayHash("/STACK"); displayHash("/STUB"); displayHash("/SUBSYSTEM"); + displayHash("/SWAPRUN"); displayHash("/TLBID"); displayHash("/TLBOUT"); + displayHash("/TSAWARE"); displayHash("/VERBOSE"); displayHash("/VERSION"); + displayHash("/VXD"); displayHash("/WS "); displayHash("/libpath"); + +#endif +#ifdef USE_DISPLAY_HASH + // Sub options + displayHash("UNLOAD"); displayHash("NOBIND"); displayHash("no"); displayHash("NOSTATUS"); displayHash("STATUS"); + displayHash("AM33"); displayHash("ARM"); displayHash("CEE"); displayHash("EBC"); displayHash("IA64"); displayHash("X86"); displayHash("X64"); displayHash("M32R"); + displayHash("MIPS"); displayHash("MIPS16"); displayHash("MIPSFPU"); displayHash("MIPSFPU16"); displayHash("MIPSR41XX"); displayHash("PPC"); + displayHash("SH3"); displayHash("SH3DSP"); displayHash("SH4"); displayHash("SH5"); displayHash("THUMB"); displayHash("TRICORE"); displayHash("EXPORTS"); + displayHash("LINES"); displayHash("REF"); displayHash("NOREF"); displayHash("ICF"); displayHash("WIN98"); displayHash("NOWIN98"); + displayHash("CONSOLE"); displayHash("EFI_APPLICATION"); displayHash("EFI_BOOT_SERVICE_DRIVER"); displayHash("EFI_ROM"); displayHash("EFI_RUNTIME_DRIVER"); displayHash("NATIVE"); + displayHash("POSIX"); displayHash("WINDOWS"); displayHash("WINDOWSCE"); displayHash("NET"); displayHash("CD"); displayHash("NO"); +#endif + bool found = true; + switch (elfHash(option)) { + case 0x3360dbe: // /ALIGN[:number] + case 0x1485c34: // /ALLOWBIND[:NO] + case 0x6b21972: // /DEFAULTLIB:library + case 0x396ea92: // /DRIVER[:UPONLY | :WDM] + case 0xaca9d75: // /EXETYPE[:DYNAMIC | :DEV386] + case 0x3ad5444: // /EXPORT:entryname[,@ordinal[,NONAME]][,DATA] + case 0x33aec94: // /FIXED[:NO] + case 0x33b4675: // /FORCE:[MULTIPLE|UNRESOLVED] + case 0x3dc3455: // /IGNORE:number,number,number,number ### NOTE: This one is undocumented, but it is even used by Microsoft. + // In recent versions of the Microsoft linker they have disabled this undocumented feature. + case 0x7988f7e: // /SECTION:name,[E][R][W][S][D][K][L][P][X][,ALIGN=#] + case 0x0348992: // /STUB:filename + case 0x0034bc4: // /VXD + case 0x0034c50: // /WS + AdditionalOptions += option; + break; + case 0x679c075: // /ASSEMBLYMODULE:filename + AddModuleNamesToAssembly += option+15; + break; + case 0x062d065: // /ASSEMBLYRESOURCE:filename + LinkToManagedResourceFile = option+18; + break; + case 0x0336675: // /BASE:{address | @filename,key} + // Do we need to do a manual lookup when '@filename,key'? + // Seems BaseAddress only can contain the location... + // We don't use it in Qt, so keep it simple for now + BaseAddress = option+6; + break; + case 0x3389797: // /DEBUG + GenerateDebugInformation = _True; + break; + case 0x0033896: // /DEF:filename + ModuleDefinitionFile = option+5; + break; + case 0x338a069: // /DELAY:{UNLOAD | NOBIND} + // MS documentation does not specify what to do with + // this option, so we'll put it in AdditionalOptions + AdditionalOptions += option; + break; + case 0x06f4bf4: // /DELAYLOAD:dllname + DelayLoadDLLs += option+11; + break; + case 0x003390c: // /DLL + // This option is not used for vcproj files + break; + case 0x33a3979: // /ENTRY:function + EntryPointSymbol = option+7; + break; + case 0x033c960: // /HEAP:reserve[,commit] + { + QStringList both = QString(option+6).split(","); + HeapReserveSize = both[0].toLongLong(); + if(both.count() == 2) + HeapCommitSize = both[1].toLongLong(); + } + break; + case 0x3d91494: // /IDLOUT:[path\]filename + MergedIDLBaseFileName = option+8; + break; + case 0x345a04c: // /IGNOREIDL + IgnoreEmbeddedIDL = _True; + break; + case 0x3e250e2: // /IMPLIB:filename + ImportLibrary = option+8; + break; + case 0xe281ab5: // /INCLUDE:symbol + ForceSymbolReferences += option+9; + break; + case 0xb28103c: // /INCREMENTAL[:no] + if(*(option+12) == ':' && + (*(option+13) == 'n' || *(option+13) == 'N')) + LinkIncremental = linkIncrementalNo; + else + LinkIncremental = linkIncrementalYes; + break; + case 0x26e4675: // /LARGEADDRESSAWARE[:no] + if(*(option+18) == ':' && + *(option+19) == 'n') + LargeAddressAware = addrAwareNoLarge; + else + LargeAddressAware = addrAwareLarge; + break; + case 0x2f96bc8: // /libpath:dir + case 0x0d745c8: // /LIBPATH:dir + AdditionalLibraryDirectories += option+9; + break; + case 0x0341877: // /LTCG[:NOSTATUS|:STATUS] + config->WholeProgramOptimization = _True; + if (config->CompilerVersion >= NET2005) { + LinkTimeCodeGeneration = optLTCGEnabled; + if(*(option+5) == ':') { + const char* str = option+6; + if (*str == 'S') + ShowProgress = linkProgressAll; +#ifndef Q_OS_WIN + else if (strncasecmp(str, "pginstrument", 12)) + LinkTimeCodeGeneration = optLTCGInstrument; + else if (strncasecmp(str, "pgoptimize", 10)) + LinkTimeCodeGeneration = optLTCGOptimize; + else if (strncasecmp(str, "pgupdate", 8 )) + LinkTimeCodeGeneration = optLTCGUpdate; +#else + else if (_stricmp(str, "pginstrument")) + LinkTimeCodeGeneration = optLTCGInstrument; + else if (_stricmp(str, "pgoptimize")) + LinkTimeCodeGeneration = optLTCGOptimize; + else if (_stricmp(str, "pgupdate")) + LinkTimeCodeGeneration = optLTCGUpdate; +#endif + } + } else { + AdditionalOptions.append(option); + } + break; + case 0x379ED25: + case 0x157cf65: // /MACHINE:{AM33|ARM|CEE|IA64|X86|M32R|MIPS|MIPS16|MIPSFPU|MIPSFPU16|MIPSR41XX|PPC|SH3|SH4|SH5|THUMB|TRICORE} + switch (elfHash(option+9)) { + // Very limited documentation on all options but X86, + case 0x0005bb6: // X86 + TargetMachine = machineX86; + break; + // so we put the others in AdditionalOptions... + case 0x0005b94: // X64 + case 0x0046063: // AM33 + case 0x000466d: // ARM + case 0x0004795: // CEE + case 0x0004963: // EBC + case 0x004d494: // IA64 + case 0x0050672: // M32R + case 0x0051e53: // MIPS + case 0x51e5646: // MIPS16 + case 0x1e57b05: // MIPSFPU + case 0x57b09a6: // MIPSFPU16 + case 0x5852738: // MIPSR41XX + case 0x0005543: // PPC + case 0x00057b3: // SH3 + case 0x57b7980: // SH3DSP + case 0x00057b4: // SH4 + case 0x00057b5: // SH5 + case 0x058da12: // THUMB + case 0x96d8435: // TRICORE + default: + AdditionalOptions += option; + break; + } + break; + case 0x0034160: // /MAP[:filename] + GenerateMapFile = _True; + MapFileName = option+5; + break; + case 0x164e1ef: // /MAPINFO:{EXPORTS|LINES} + if(*(option+9) == 'E') + MapExports = _True; + else if(*(option+9) == 'L') + MapLines = _True; + break; + case 0x341a6b5: // /MERGE:from=to + MergeSections = option+7; + break; + case 0x0341d8c: // /MIDL:@file + MidlCommandFile = option+7; + break; + case 0x84e2679: // /NOASSEMBLY + TurnOffAssemblyGeneration = _True; + break; + case 0x2b21942: // /NODEFAULTLIB[:library] + if(*(option+13) == '\0') + IgnoreAllDefaultLibraries = _True; + else + IgnoreDefaultLibraryNames += option+14; + break; + case 0x33a3a39: // /NOENTRY + ResourceOnlyDLL = _True; + break; + case 0x434138f: // /NOLOGO + SuppressStartupBanner = _True; + break; + case 0x0034454: // /OPT:{REF | NOREF | ICF[=iterations] | NOICF | WIN98 | NOWIN98} + { + char third = *(option+7); + switch (third) { + case 'F': // REF + if(*(option+5) == 'R') { + OptimizeReferences = optReferences; + } else { // ICF[=iterations] + EnableCOMDATFolding = optFolding; + // [=iterations] case is not documented + } + break; + case 'R': // NOREF + OptimizeReferences = optNoReferences; + break; + case 'I': // NOICF + EnableCOMDATFolding = optNoFolding; + break; + case 'N': // WIN98 + OptimizeForWindows98 = optWin98Yes; + break; + case 'W': // NOWIN98 + OptimizeForWindows98 = optWin98No; + break; + default: + found = false; + } + } + break; + case 0x34468a2: // /ORDER:@filename + FunctionOrder = option+8; + break; + case 0x00344a4: // /OUT:filename + OutputFile = option+5; + break; + case 0x0034482: // /PDB:filename + ProgramDatabaseFile = option+5; + break; + case 0xa2ad314: // /PDBSTRIPPED:pdb_file_name + StripPrivateSymbols = option+13; + break; + case 0x6a09535: // /RELEASE + SetChecksum = _True; + break; + case 0x348857b: // /STACK:reserve[,commit] + { + QStringList both = QString(option+7).split(","); + StackReserveSize = both[0].toLongLong(); + if(both.count() == 2) + StackCommitSize = both[1].toLongLong(); + } + break; + case 0x75AA4D8: // /SAFESH:{NO} + { + AdditionalOptions += option; + break; + } + case 0x9B3C00D: + case 0x78dc00d: // /SUBSYSTEM:{CONSOLE|EFI_APPLICATION|EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER|NATIVE|POSIX|WINDOWS|WINDOWSCE}[,major[.minor]] + { + // Split up in subsystem, and version number + QStringList both = QString(option+11).split(","); + switch (elfHash(both[0].toLatin1())) { + case 0x8438445: // CONSOLE + SubSystem = subSystemConsole; + break; + case 0xbe29493: // WINDOWS + SubSystem = subSystemWindows; + break; + // The following are undocumented, so add them to AdditionalOptions + case 0x240949e: // EFI_APPLICATION + case 0xe617652: // EFI_BOOT_SERVICE_DRIVER + case 0x9af477d: // EFI_ROM + case 0xd34df42: // EFI_RUNTIME_DRIVER + case 0x5268ea5: // NATIVE + case 0x05547e8: // POSIX + case 0x2949c95: // WINDOWSCE + case 0x4B69795: // windowsce + AdditionalOptions += option; + break; + default: + found = false; + } + } + break; + case 0x8b654de: // /SWAPRUN:{NET | CD} + if(*(option+9) == 'N') + SwapRunFromNet = _True; + else if(*(option+9) == 'C') + SwapRunFromCD = _True; + else + found = false; + break; + case 0x34906d4: // /TLBID:id + TypeLibraryResourceID = QString(option+7).toLongLong(); + break; + case 0x4907494: // /TLBOUT:[path\]filename + TypeLibraryFile = option+8; + break; + case 0x976b525: // /TSAWARE[:NO] + if(*(option+8) == ':') + TerminalServerAware = termSvrAwareNo; + else + TerminalServerAware = termSvrAwareYes; + break; + case 0xaa67735: // /VERBOSE[:lib] + if(*(option+9) == ':') { + ShowProgress = linkProgressLibs; + AdditionalOptions += option; + } else { + ShowProgress = linkProgressAll; + } + break; + case 0xaa77f7e: // /VERSION:major[.minor] + Version = option+9; + break; + default: + AdditionalOptions += option; + break; + } + if(!found) { + warn_msg(WarnLogic, "Could not parse Linker options: %s, added as AdditionalOption", option); + AdditionalOptions += option; + } + return found; +} + +// VCMIDLTool ------------------------------------------------------- +VCMIDLTool::VCMIDLTool() + : DefaultCharType(midlCharUnsigned), + EnableErrorChecks(midlDisableAll), + ErrorCheckAllocations(unset), + ErrorCheckBounds(unset), + ErrorCheckEnumRange(unset), + ErrorCheckRefPointers(unset), + ErrorCheckStubData(unset), + GenerateStublessProxies(unset), + GenerateTypeLibrary(unset), + IgnoreStandardIncludePath(unset), + MkTypLibCompatible(unset), + StructMemberAlignment(midlAlignNotSet), + SuppressStartupBanner(unset), + TargetEnvironment(midlTargetNotSet), + ValidateParameters(unset), + WarnAsError(unset), + WarningLevel(midlWarningLevel_0) +{ +} + +XmlOutput &operator<<(XmlOutput &xml, const VCMIDLTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, _VCMIDLTool) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_CPreprocessOptions, tool.CPreprocessOptions) + << attrE(_DefaultCharType, tool.DefaultCharType) + << attrS(_DLLDataFileName, tool.DLLDataFileName) + << attrE(_EnableErrorChecks, tool.EnableErrorChecks) + << attrT(_ErrorCheckAllocations, tool.ErrorCheckAllocations) + << attrT(_ErrorCheckBounds, tool.ErrorCheckBounds) + << attrT(_ErrorCheckEnumRange, tool.ErrorCheckEnumRange) + << attrT(_ErrorCheckRefPointers, tool.ErrorCheckRefPointers) + << attrT(_ErrorCheckStubData, tool.ErrorCheckStubData) + << attrX(_FullIncludePath, tool.FullIncludePath) + << attrT(_GenerateStublessProxies, tool.GenerateStublessProxies) + << attrT(_GenerateTypeLibrary, tool.GenerateTypeLibrary) + << attrS(_HeaderFileName, tool.HeaderFileName) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrS(_InterfaceIdentifierFileName, tool.InterfaceIdentifierFileName) + << attrT(_MkTypLibCompatible, tool.MkTypLibCompatible) + << attrS(_OutputDirectory, tool.OutputDirectory) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << attrS(_ProxyFileName, tool.ProxyFileName) + << attrS(_RedirectOutputAndErrors, tool.RedirectOutputAndErrors) + << attrE(_StructMemberAlignment, tool.StructMemberAlignment, /*ifNot*/ midlAlignNotSet) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrE(_TargetEnvironment, tool.TargetEnvironment, /*ifNot*/ midlTargetNotSet) + << attrS(_TypeLibraryName, tool.TypeLibraryName) + << attrX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions) + << attrT(_ValidateParameters, tool.ValidateParameters) + << attrT(_WarnAsError, tool.WarnAsError) + << attrE(_WarningLevel, tool.WarningLevel) + << closetag(_Tool); +} + +bool VCMIDLTool::parseOption(const char* option) +{ +#ifdef USE_DISPLAY_HASH + displayHash("/D name[=def]"); displayHash("/I directory-list"); displayHash("/Oi"); + displayHash("/Oic"); displayHash("/Oicf"); displayHash("/Oif"); displayHash("/Os"); + displayHash("/U name"); displayHash("/WX"); displayHash("/W{0|1|2|3|4}"); + displayHash("/Zp {N}"); displayHash("/Zs"); displayHash("/acf filename"); + displayHash("/align {N}"); displayHash("/app_config"); displayHash("/c_ext"); + displayHash("/char ascii7"); displayHash("/char signed"); displayHash("/char unsigned"); + displayHash("/client none"); displayHash("/client stub"); displayHash("/confirm"); + displayHash("/cpp_cmd cmd_line"); displayHash("/cpp_opt options"); + displayHash("/cstub filename"); displayHash("/dlldata filename"); displayHash("/env win32"); + displayHash("/env win64"); displayHash("/error all"); displayHash("/error allocation"); + displayHash("/error bounds_check"); displayHash("/error enum"); displayHash("/error none"); + displayHash("/error ref"); displayHash("/error stub_data"); displayHash("/h filename"); + displayHash("/header filename"); displayHash("/iid filename"); displayHash("/lcid"); + displayHash("/mktyplib203"); displayHash("/ms_ext"); displayHash("/ms_union"); + displayHash("/msc_ver <nnnn>"); displayHash("/newtlb"); displayHash("/no_cpp"); + displayHash("/no_def_idir"); displayHash("/no_default_epv"); displayHash("/no_format_opt"); + displayHash("/no_warn"); displayHash("/nocpp"); displayHash("/nologo"); displayHash("/notlb"); + displayHash("/o filename"); displayHash("/oldnames"); displayHash("/oldtlb"); + displayHash("/osf"); displayHash("/out directory"); displayHash("/pack {N}"); + displayHash("/prefix all"); displayHash("/prefix client"); displayHash("/prefix server"); + displayHash("/prefix switch"); displayHash("/protocol all"); displayHash("/protocol dce"); + displayHash("/protocol ndr64"); displayHash("/proxy filename"); displayHash("/robust"); + displayHash("/rpcss"); displayHash("/savePP"); displayHash("/server none"); + displayHash("/server stub"); displayHash("/sstub filename"); displayHash("/syntax_check"); + displayHash("/target {system}"); displayHash("/tlb filename"); displayHash("/use_epv"); + displayHash("/win32"); displayHash("/win64"); +#endif + bool found = true; + int offset = 0; + switch(elfHash(option)) { + case 0x0000334: // /D name[=def] + PreprocessorDefinitions += option+3; + break; + case 0x0000339: // /I directory-list + AdditionalIncludeDirectories += option+3; + break; + case 0x0345f96: // /Oicf + case 0x00345f6: // /Oif + GenerateStublessProxies = _True; + break; + case 0x0000345: // /U name + UndefinePreprocessorDefinitions += option+3; + break; + case 0x00034c8: // /WX + WarnAsError = _True; + break; + case 0x3582fde: // /align {N} + offset = 3; // Fallthrough + case 0x0003510: // /Zp {N} + switch (*(option+offset+4)) { + case '1': + StructMemberAlignment = (*(option+offset+5) == '\0') ? midlAlignSingleByte : midlAlignSixteenBytes; + break; + case '2': + StructMemberAlignment = midlAlignTwoBytes; + break; + case '4': + StructMemberAlignment = midlAlignFourBytes; + break; + case '8': + StructMemberAlignment = midlAlignEightBytes; + break; + default: + found = false; + } + break; + case 0x0359e82: // /char {ascii7|signed|unsigned} + switch(*(option+6)) { + case 'a': + DefaultCharType = midlCharAscii7; + break; + case 's': + DefaultCharType = midlCharSigned; + break; + case 'u': + DefaultCharType = midlCharUnsigned; + break; + default: + found = false; + } + break; + case 0xa766524: // /cpp_opt options + CPreprocessOptions += option+9; + break; + case 0xb32abf1: // /dlldata filename + DLLDataFileName = option + 9; + break; + case 0x0035c56: // /env {win32|win64} + TargetEnvironment = (*(option+8) == '6') ? midlTargetWin64 : midlTargetWin32; + break; + case 0x35c9962: // /error {all|allocation|bounds_check|enum|none|ref|stub_data} + EnableErrorChecks = midlEnableCustom; + switch (*(option+7)) { + case 'a': + if(*(option+10) == '\0') + EnableErrorChecks = midlEnableAll; + else + ErrorCheckAllocations = _True; + break; + case 'b': + ErrorCheckBounds = _True; + break; + case 'e': + ErrorCheckEnumRange = _True; + break; + case 'n': + EnableErrorChecks = midlDisableAll; + break; + case 'r': + ErrorCheckRefPointers = _True; + break; + case 's': + ErrorCheckStubData = _True; + break; + default: + found = false; + } + break; + case 0x5eb7af2: // /header filename + offset = 5; + case 0x0000358: // /h filename + HeaderFileName = option + offset + 3; + break; + case 0x0035ff4: // /iid filename + InterfaceIdentifierFileName = option+5; + break; + case 0x64b7933: // /mktyplib203 + MkTypLibCompatible = _True; + break; + case 0x8e0b0a2: // /no_def_idir + IgnoreStandardIncludePath = _True; + break; + case 0x65635ef: // /nologo + SuppressStartupBanner = _True; + break; + case 0x3656b22: // /notlb + GenerateTypeLibrary = _True; + break; + case 0x000035f: // /o filename + RedirectOutputAndErrors = option+3; + break; + case 0x00366c4: // /out directory + OutputDirectory = option+5; + break; + case 0x36796f9: // /proxy filename + ProxyFileName = option+7; + break; + case 0x6959c94: // /robust + ValidateParameters = _True; + break; + case 0x6a88df4: // /target {system} + if(*(option+11) == '6') + TargetEnvironment = midlTargetWin64; + else + TargetEnvironment = midlTargetWin32; + break; + case 0x0036b22: // /tlb filename + TypeLibraryName = option+5; + break; + case 0x36e0162: // /win32 + TargetEnvironment = midlTargetWin32; + break; + case 0x36e0194: // /win64 + TargetEnvironment = midlTargetWin64; + break; + case 0x0003459: // /Oi + case 0x00345f3: // /Oic + case 0x0003463: // /Os + case 0x0003513: // /Zs + case 0x0035796: // /acf filename + case 0x5b1cb97: // /app_config + case 0x3595cf4: // /c_ext + case 0x5a2fc64: // /client {none|stub} + case 0xa64d3dd: // /confirm + case 0xa765b64: // /cpp_cmd cmd_line + case 0x35aabb2: // /cstub filename + case 0x03629f4: // /lcid + case 0x6495cc4: // /ms_ext + case 0x96c7a1e: // /ms_union + case 0x4996fa2: // /msc_ver <nnnn> + case 0x64ceb12: // /newtlb + case 0x6555a40: // /no_cpp + case 0xf64d6a6: // /no_default_epv + case 0x6dd9384: // /no_format_opt + case 0x556dbee: // /no_warn + case 0x3655a70: // /nocpp + case 0x2b455a3: // /oldnames + case 0x662bb12: // /oldtlb + case 0x0036696: // /osf + case 0x036679b: // /pack {N} + case 0x678bd38: // /prefix {all|client|server|switch} + case 0x96b702c: // /protocol {all|dce|ndr64} + case 0x3696aa3: // /rpcss + case 0x698ca60: // /savePP + case 0x69c9cf2: // /server {none|stub} + case 0x36aabb2: // /sstub filename + case 0xce9b12b: // /syntax_check + case 0xc9b5f16: // /use_epv + AdditionalOptions += option; + break; + default: + // /W{0|1|2|3|4} case + if(*(option+1) == 'W') { + switch (*(option+2)) { + case '0': + WarningLevel = midlWarningLevel_0; + break; + case '1': + WarningLevel = midlWarningLevel_1; + break; + case '2': + WarningLevel = midlWarningLevel_2; + break; + case '3': + WarningLevel = midlWarningLevel_3; + break; + case '4': + WarningLevel = midlWarningLevel_4; + break; + default: + found = false; + } + } + break; + } + if(!found) + warn_msg(WarnLogic, "Could not parse MIDL option: %s", option); + return true; +} + +// VCLibrarianTool -------------------------------------------------- +VCLibrarianTool::VCLibrarianTool() + : IgnoreAllDefaultLibraries(unset), + SuppressStartupBanner(_True) +{ +} + +XmlOutput &operator<<(XmlOutput &xml, const VCLibrarianTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, _VCLibrarianTool) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies) + << attrX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_ExportNamedFunctions, tool.ExportNamedFunctions) + << attrX(_ForceSymbolReferences, tool.ForceSymbolReferences) + << attrT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrX(_IgnoreDefaultLibraryNames, tool.IgnoreDefaultLibraryNames) + << attrS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) + << attrS(_OutputFile, tool.OutputFile) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << closetag(_Tool); +} + +// VCCustomBuildTool ------------------------------------------------ +VCCustomBuildTool::VCCustomBuildTool() +{ + ToolName = "VCCustomBuildTool"; +} + +XmlOutput &operator<<(XmlOutput &xml, const VCCustomBuildTool &tool) +{ + // The code below offers two ways to split custom build step commands. + // Normally the $$escape_expand(\n\t) is used in a project file, which is correctly translated + // in all generators. However, if you use $$escape_expand(\n\r) (or \n\h) instead, the VCPROJ + // generator will instead of binding the commands with " && " will insert a proper newline into + // the VCPROJ file. We sometimes use this method of splitting commands if the custom buildstep + // contains a command-line which is too big to run on certain OS. + QString cmds; + int end = tool.CommandLine.count(); + for(int i = 0; i < end; ++i) { + QString cmdl = tool.CommandLine.at(i); + if (cmdl.contains("\r\t")) { + if (i == end - 1) + cmdl = cmdl.trimmed(); + cmdl.replace("\r\t", " && "); + } else if (cmdl.contains("\r\n")) { + ; + } else if (cmdl.contains("\r\\h")) { + // The above \r\n should work, but doesn't, so we have this hack + cmdl.replace("\r\\h", "\r\n"); + } else { + if (i < end - 1) + cmdl += " && "; + } + cmds += cmdl; + } + return xml + << tag(_Tool) + << attrS(_Name, tool.ToolName) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies, ";") + << attrS(_CommandLine, cmds) + << attrS(_Description, tool.Description) + << attrX(_Outputs, tool.Outputs, ";") + << attrS(_Path, tool.ToolPath) + << closetag(_Tool); +} + +// VCResourceCompilerTool ------------------------------------------- +VCResourceCompilerTool::VCResourceCompilerTool() + : Culture(rcUseDefault), + IgnoreStandardIncludePath(unset), + ShowProgress(linkProgressNotSet) +{ + PreprocessorDefinitions = QStringList("NDEBUG"); +} + +XmlOutput &operator<<(XmlOutput &xml, const VCResourceCompilerTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, _VCResourceCompilerTool) + << attrS(_Path, tool.ToolPath) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrE(_Culture, tool.Culture, /*ifNot*/ rcUseDefault) + << attrX(_FullIncludePath, tool.FullIncludePath) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << attrS(_ResourceOutputFileName, tool.ResourceOutputFileName) + << attrE(_ShowProgress, tool.ShowProgress, /*ifNot*/ linkProgressNotSet) + << closetag(_Tool); +} + +// VCDeploymentTool -------------------------------------------- +VCDeploymentTool::VCDeploymentTool() + : RegisterOutput(registerNo) +{ + DeploymentTag = "DeploymentTool"; + RemoteDirectory = ""; +} + +XmlOutput &operator<<(XmlOutput &xml, const VCDeploymentTool &tool) +{ + if (tool.AdditionalFiles.isEmpty()) + return xml; + return xml + << tag(tool.DeploymentTag) + << attrS(_RemoteDirectory, tool.RemoteDirectory) + << attrE(_RegisterOutput, tool.RegisterOutput) + << attrS(_AdditionalFiles, tool.AdditionalFiles) + << closetag(tool.DeploymentTag); +} + +// VCEventTool ------------------------------------------------- +XmlOutput &operator<<(XmlOutput &xml, const VCEventTool &tool) +{ + return xml + << tag(_Tool) + << attrS(_Name, tool.ToolName) + << attrS(_Path, tool.ToolPath) + << attrS(_CommandLine, tool.CommandLine) + << attrS(_Description, tool.Description) + << attrT(_ExcludedFromBuild, tool.ExcludedFromBuild) + << closetag(_Tool); +} + +// VCPostBuildEventTool --------------------------------------------- +VCPostBuildEventTool::VCPostBuildEventTool() +{ + ToolName = "VCPostBuildEventTool"; +} + +// VCPreBuildEventTool ---------------------------------------------- +VCPreBuildEventTool::VCPreBuildEventTool() +{ + ToolName = "VCPreBuildEventTool"; +} + +// VCPreLinkEventTool ----------------------------------------------- +VCPreLinkEventTool::VCPreLinkEventTool() +{ + ToolName = "VCPreLinkEventTool"; +} + +// VCConfiguration -------------------------------------------------- + +VCConfiguration::VCConfiguration() + : ATLMinimizesCRunTimeLibraryUsage(unset), + BuildBrowserInformation(unset), + CharacterSet(charSetNotSet), + ConfigurationType(typeApplication), + RegisterOutput(unset), + UseOfATL(useATLNotSet), + UseOfMfc(useMfcStdWin), + WholeProgramOptimization(unset) +{ + compiler.config = this; + linker.config = this; + idl.config = this; +} + +XmlOutput &operator<<(XmlOutput &xml, const VCConfiguration &tool) +{ + xml << tag(_Configuration) + << attrS(_Name, tool.Name) + << attrS(_OutputDirectory, tool.OutputDirectory) + << attrT(_ATLMinimizesCRunTimeLibraryUsage, tool.ATLMinimizesCRunTimeLibraryUsage) + << attrT(_BuildBrowserInformation, tool.BuildBrowserInformation) + << attrE(_CharacterSet, tool.CharacterSet, /*ifNot*/ charSetNotSet) + << attrE(_ConfigurationType, tool.ConfigurationType) + << attrS(_DeleteExtensionsOnClean, tool.DeleteExtensionsOnClean) + << attrS(_ImportLibrary, tool.ImportLibrary) + << attrS(_IntermediateDirectory, tool.IntermediateDirectory) + << attrS(_PrimaryOutput, tool.PrimaryOutput) + << attrS(_ProgramDatabase, tool.ProgramDatabase) + << attrT(_RegisterOutput, tool.RegisterOutput) + << attrE(_UseOfATL, tool.UseOfATL, /*ifNot*/ useATLNotSet) + << attrE(_UseOfMfc, tool.UseOfMfc) + << attrT(_WholeProgramOptimization, tool.WholeProgramOptimization) + << tool.compiler + << tool.custom; + if (tool.ConfigurationType == typeStaticLibrary) + xml << tool.librarian; + else + xml << tool.linker; + xml << tool.idl + << tool.postBuild + << tool.preBuild + << tool.preLink + << tool.resource + << tool.deployment + << closetag(_Configuration); + return xml; +} +// VCFilter --------------------------------------------------------- +VCFilter::VCFilter() + : ParseFiles(unset), + Config(0) +{ + useCustomBuildTool = false; + useCompilerTool = false; +} + +void VCFilter::addFile(const QString& filename) +{ + Files += VCFilterFile(filename); +} + +void VCFilter::addFile(const VCFilterFile& fileInfo) +{ + Files += VCFilterFile(fileInfo); +} + +void VCFilter::addFiles(const QStringList& fileList) +{ + for (int i = 0; i < fileList.count(); ++i) + addFile(fileList.at(i)); +} + +void VCFilter::modifyPCHstage(QString str) +{ + bool autogenSourceFile = Project->autogenPrecompCPP; + bool pchThroughSourceFile = !Project->precompCPP.isEmpty(); + bool isCFile = false; + for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) { + if (str.endsWith(*it)) { + isCFile = true; + break; + } + } + bool isHFile = str.endsWith(".h") && (str == Project->precompH); + bool isCPPFile = pchThroughSourceFile && (str == Project->precompCPP); + + if(!isCFile && !isHFile && !isCPPFile) + return; + + if(isHFile && pchThroughSourceFile) { + if (autogenSourceFile) { + useCustomBuildTool = true; + QString toFile(Project->precompCPP); + CustomBuildTool.Description = "Generating precompiled header source file '" + toFile + "' ..."; + CustomBuildTool.Outputs += toFile; + + QStringList lines; + CustomBuildTool.CommandLine += + "echo /*-------------------------------------------------------------------- >" + toFile; + lines << "* Precompiled header source file used by Visual Studio.NET to generate"; + lines << "* the .pch file."; + lines << "*"; + lines << "* Due to issues with the dependencies checker within the IDE, it"; + lines << "* sometimes fails to recompile the PCH file, if we force the IDE to"; + lines << "* create the PCH file directly from the header file."; + lines << "*"; + lines << "* This file is auto-generated by qmake since no PRECOMPILED_SOURCE was"; + lines << "* specified, and is used as the common stdafx.cpp. The file is only"; + lines << "* generated when creating .vcproj project files, and is not used for"; + lines << "* command line compilations by nmake."; + lines << "*"; + lines << "* WARNING: All changes made in this file will be lost."; + lines << "--------------------------------------------------------------------*/"; + lines << "#include \"" + Project->precompHFilename + "\""; + foreach(QString line, lines) + CustomBuildTool.CommandLine += "echo " + line + ">>" + toFile; + } + return; + } + + useCompilerTool = true; + // Setup PCH options + CompilerTool.UsePrecompiledHeader = (isCFile ? pchNone : pchCreateUsingSpecific); + CompilerTool.PrecompiledHeaderThrough = (isCPPFile ? QString("$(INHERIT)") : QString("$(NOINHERIT)")); + CompilerTool.ForcedIncludeFiles = QStringList("$(NOINHERIT)"); +} + +bool VCFilter::addExtraCompiler(const VCFilterFile &info) +{ + const QStringList &extraCompilers = Project->extraCompilerSources.value(info.file); + if (extraCompilers.isEmpty()) + return false; + + QString inFile = info.file; + + // is the extracompiler rule on a file with a built in compiler? + const QStringList &objectMappedFile = Project->extraCompilerOutputs[inFile]; + bool hasBuiltIn = false; + if (!objectMappedFile.isEmpty()) { + hasBuiltIn = Project->hasBuiltinCompiler(objectMappedFile.at(0)); +// qDebug("*** Extra compiler file has object mapped file '%s' => '%s'", qPrintable(inFile), qPrintable(objectMappedFile.join(" "))); + } + + CustomBuildTool.AdditionalDependencies.clear(); + CustomBuildTool.CommandLine.clear(); + CustomBuildTool.Description.clear(); + CustomBuildTool.Outputs.clear(); + CustomBuildTool.ToolPath.clear(); + CustomBuildTool.ToolName = QLatin1String(_VCCustomBuildTool); + + for (int x = 0; x < extraCompilers.count(); ++x) { + const QString &extraCompilerName = extraCompilers.at(x); + + if (!Project->verifyExtraCompiler(extraCompilerName, inFile) && !hasBuiltIn) + continue; + + // All information about the extra compiler + QString tmp_out = Project->project->first(extraCompilerName + ".output"); + QString tmp_cmd = Project->project->variables()[extraCompilerName + ".commands"].join(" "); + QString tmp_cmd_name = Project->project->variables()[extraCompilerName + ".name"].join(" "); + QStringList tmp_dep = Project->project->variables()[extraCompilerName + ".depends"]; + QString tmp_dep_cmd = Project->project->variables()[extraCompilerName + ".depend_command"].join(" "); + QStringList vars = Project->project->variables()[extraCompilerName + ".variables"]; + QStringList configs = Project->project->variables()[extraCompilerName + ".CONFIG"]; + bool combined = configs.indexOf("combine") != -1; + + QString cmd, cmd_name, out; + QStringList deps, inputs; + // Variabel replacement of output name + out = Option::fixPathToTargetOS( + Project->replaceExtraCompilerVariables(tmp_out, inFile, QString()), + false); + + // If file has built-in compiler, we've swapped the input and output of + // the command, as we in Visual Studio cannot have a Custom Buildstep on + // a file which uses a built-in compiler. We would in this case only get + // the result from the extra compiler. If 'hasBuiltIn' is true, we know + // that we're actually on the _output_file_ of the result, and we + // therefore swap inFile and out below, since the extra-compiler still + // must see it as the original way. If the result also has a built-in + // compiler, too bad.. + if (hasBuiltIn) { + out = inFile; + inFile = objectMappedFile.at(0); + } + + // Dependency for the output + if(!tmp_dep.isEmpty()) + deps = tmp_dep; + if(!tmp_dep_cmd.isEmpty()) { + // Execute dependency command, and add every line as a dep + char buff[256]; + QString dep_cmd = Project->replaceExtraCompilerVariables(tmp_dep_cmd, + Option::fixPathToLocalOS(inFile, true, false), + out); + if(Project->canExecute(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 extradeps = indeps.split(QLatin1Char('\n')); + for (int i = 0; i < extradeps.count(); ++i) { + QString dd = extradeps.at(i).simplified(); + if (!dd.isEmpty()) + deps += Project->fileFixify(dd); + } + } + } + } + } + for (int i = 0; i < deps.count(); ++i) + deps[i] = Option::fixPathToTargetOS( + Project->replaceExtraCompilerVariables(deps.at(i), inFile, out), + false).trimmed(); + // Command for file + if (combined) { + // Add dependencies for each file + QStringList tmp_in = Project->project->variables()[extraCompilerName + ".input"]; + for (int a = 0; a < tmp_in.count(); ++a) { + const QStringList &files = Project->project->variables()[tmp_in.at(a)]; + for (int b = 0; b < files.count(); ++b) { + deps += Project->findDependencies(files.at(b)); + inputs += Option::fixPathToTargetOS(files.at(b), false); + } + } + deps += inputs; // input files themselves too.. + + // Replace variables for command w/all input files + // ### join gives path issues with directories containing spaces! + cmd = Project->replaceExtraCompilerVariables(tmp_cmd, + inputs.join(" "), + out); + } else { + deps += inFile; // input file itself too.. + cmd = Project->replaceExtraCompilerVariables(tmp_cmd, + inFile, + out); + } + // Name for command + if(!tmp_cmd_name.isEmpty()) { + cmd_name = Project->replaceExtraCompilerVariables(tmp_cmd_name, inFile, out); + } else { + int space = cmd.indexOf(' '); + if(space != -1) + cmd_name = cmd.left(space); + else + cmd_name = cmd; + if((cmd_name[0] == '\'' || cmd_name[0] == '"') && + cmd_name[0] == cmd_name[cmd_name.length()-1]) + cmd_name = cmd_name.mid(1,cmd_name.length()-2); + } + + // Fixify paths + for (int i = 0; i < deps.count(); ++i) + deps[i] = Option::fixPathToTargetOS(deps[i], false); + + + // Output in info.additionalFile ----------- + if (!CustomBuildTool.Description.isEmpty()) + CustomBuildTool.Description += " & "; + CustomBuildTool.Description += cmd_name; + CustomBuildTool.CommandLine += cmd.trimmed().split("\n", QString::SkipEmptyParts); + int space = cmd.indexOf(' '); + QFileInfo finf(cmd.left(space)); + if (CustomBuildTool.ToolPath.isEmpty()) + CustomBuildTool.ToolPath += Option::fixPathToTargetOS(finf.path()); + CustomBuildTool.Outputs += out; + + deps += CustomBuildTool.AdditionalDependencies; + deps += cmd.left(cmd.indexOf(' ')); + // Make sure that all deps are only once + QMap<QString, bool> uniqDeps; + for (int c = 0; c < deps.count(); ++c) { + QString aDep = deps.at(c).trimmed(); + if (!aDep.isEmpty()) + uniqDeps[aDep] = false; + } + CustomBuildTool.AdditionalDependencies = uniqDeps.keys(); + } + + // Ensure that none of the output files are also dependencies. Or else, the custom buildstep + // will be rebuild every time, even if nothing has changed. + foreach(QString output, CustomBuildTool.Outputs) { + CustomBuildTool.AdditionalDependencies.removeAll(output); + } + + useCustomBuildTool = !CustomBuildTool.CommandLine.isEmpty(); + return useCustomBuildTool; +} + +void VCFilter::outputFileConfig(XmlOutput &xml, const QString &filename) +{ + // Clearing each filter tool + useCustomBuildTool = false; + useCompilerTool = false; + CustomBuildTool = VCCustomBuildTool(); + CompilerTool = VCCLCompilerTool(); + + // Unset some default options + CompilerTool.BufferSecurityCheck = unset; + CompilerTool.DebugInformationFormat = debugUnknown; + CompilerTool.ExceptionHandling = ehDefault; + CompilerTool.GeneratePreprocessedFile = preprocessUnknown; + CompilerTool.Optimization = optimizeDefault; + CompilerTool.ProgramDataBaseFileName.clear(); + CompilerTool.RuntimeLibrary = rtUnknown; + CompilerTool.WarningLevel = warningLevelUnknown; + CompilerTool.config = Config; + + bool inBuild = false; + VCFilterFile info; + for (int i = 0; i < Files.count(); ++i) { + if (Files.at(i).file == filename) { + info = Files.at(i); + inBuild = true; + } + } + inBuild &= !info.excludeFromBuild; + + if (inBuild) { + addExtraCompiler(info); + if(Project->usePCH) + modifyPCHstage(info.file); + } else { + // Excluded files uses an empty compiler stage + if(info.excludeFromBuild) + useCompilerTool = true; + } + + // Actual XML output ---------------------------------- + if(useCustomBuildTool || useCompilerTool || !inBuild) { + xml << tag(_FileConfiguration) + << attr(_Name, (*Config).Name) + << (!inBuild ? attrS(_ExcludedFromBuild, "true") : noxml()); + if (useCustomBuildTool) + xml << CustomBuildTool; + if (useCompilerTool) + xml << CompilerTool; + xml << closetag(_FileConfiguration); + } +} + +XmlOutput &operator<<(XmlOutput &xml, VCFilter &tool) +{ + if(!tool.Files.count()) + return xml; + + if (!tool.Name.isEmpty()) { + xml << tag(_Filter) + << attrS(_Name, tool.Name) + << attrS(_Filter, tool.Filter) + << attrS(_UniqueIdentifier, tool.Guid) + << attrT(_ParseFiles, tool.ParseFiles); + } + for (int i = 0; i < tool.Files.count(); ++i) { + const VCFilterFile &info = tool.Files.at(i); + xml << tag(_File) + << attrS(_RelativePath, Option::fixPathToLocalOS(info.file)) + << data(); // In case no custom builds, to avoid "/>" endings + tool.outputFileConfig(xml, tool.Files.at(i).file); + xml << closetag(_File); + } + if (!tool.Name.isEmpty()) + xml << closetag(_Filter); + return xml; +} + +// VCProjectSingleConfig -------------------------------------------- +VCFilter nullFilter; +VCFilter& VCProjectSingleConfig::filterForExtraCompiler(const QString &compilerName) +{ + for (int i = 0; i < ExtraCompilersFiles.count(); ++i) + if (ExtraCompilersFiles.at(i).Name == compilerName) + return ExtraCompilersFiles[i]; + return nullFilter; +} + + +XmlOutput &operator<<(XmlOutput &xml, const VCProjectSingleConfig &tool) +{ + xml << decl("1.0", "Windows-1252") + << tag(_VisualStudioProject) + << attrS(_ProjectType, "Visual C++") + << attrS(_Version, tool.Version) + << attrS(_Name, tool.Name) + << attrS(_ProjectGUID, tool.ProjectGUID) + << attrS(_Keyword, tool.Keyword) + << attrS(_SccProjectName, tool.SccProjectName) + << attrS(_SccLocalPath, tool.SccLocalPath) + << tag(_Platforms) + << tag(_Platform) + << attrS(_Name, tool.PlatformName) + << closetag(_Platforms) + << tag(_Configurations) + << tool.Configuration; + xml << closetag(_Configurations) + << tag(_Files); + // Add this configuration into a multi-config project, since that's where we have the flat/tree + // XML output functionality + VCProject tempProj; + tempProj.SingleProjects += tool; + tempProj.outputFilter(xml, "Sources"); + tempProj.outputFilter(xml, "Headers"); + tempProj.outputFilter(xml, "GeneratedFiles"); + tempProj.outputFilter(xml, "LexYaccFiles"); + tempProj.outputFilter(xml, "TranslationFiles"); + tempProj.outputFilter(xml, "FormFiles"); + tempProj.outputFilter(xml, "ResourceFiles"); + for (int x = 0; x < tempProj.ExtraCompilers.count(); ++x) { + tempProj.outputFilter(xml, tempProj.ExtraCompilers.at(x)); + } + tempProj.outputFilter(xml, "RootFiles"); + xml << closetag(_Files) + << tag(_Globals) + << data(); // No "/>" end tag + return xml; +} + + +// Tree file generation --------------------------------------------- +void TreeNode::generateXML(XmlOutput &xml, const QString &tagName, VCProject &tool, const QString &filter) { + if (children.size()) { + // Filter + ChildrenMap::ConstIterator it, end = children.constEnd(); + if (!tagName.isEmpty()) { + xml << tag("Filter") + << attr("Name", tagName) + << attr("Filter", ""); + } + // First round, do nested filters + for (it = children.constBegin(); it != end; ++it) + if ((*it)->children.size()) + (*it)->generateXML(xml, it.key(), tool, filter); + // Second round, do leafs + for (it = children.constBegin(); it != end; ++it) + if (!(*it)->children.size()) + (*it)->generateXML(xml, it.key(), tool, filter); + + if (!tagName.isEmpty()) + xml << closetag("Filter"); + } else { + // Leaf + tool.outputFileConfigs(xml, info, filter); + } +} + + +// Flat file generation --------------------------------------------- +void FlatNode::generateXML(XmlOutput &xml, const QString &/*tagName*/, VCProject &tool, const QString &filter) { + if (children.size()) { + ChildrenMapFlat::ConstIterator it = children.constBegin(); + ChildrenMapFlat::ConstIterator end = children.constEnd(); + for (; it != end; ++it) { + tool.outputFileConfigs(xml, (*it), filter); + } + } +} + + +// VCProject -------------------------------------------------------- +// Output all configurations (by filtername) for a file (by info) +// A filters config output is in VCFilter.outputFileConfig() +void VCProject::outputFileConfigs(XmlOutput &xml, +// VCProjectSingleConfig::FilterTypes type + const VCFilterFile &info, + const QString &filtername) +{ + xml << tag(_File) + << attrS(_RelativePath, Option::fixPathToLocalOS(info.file)); + for (int i = 0; i < SingleProjects.count(); ++i) { + VCFilter filter; + if (filtername == "RootFiles") { + filter = SingleProjects.at(i).RootFiles; + } else if (filtername == "Sources") { + filter = SingleProjects.at(i).SourceFiles; + } else if (filtername == "Headers") { + filter = SingleProjects.at(i).HeaderFiles; + } else if (filtername == "GeneratedFiles") { + filter = SingleProjects.at(i).GeneratedFiles; + } else if (filtername == "LexYaccFiles") { + filter = SingleProjects.at(i).LexYaccFiles; + } else if (filtername == "TranslationFiles") { + filter = SingleProjects.at(i).TranslationFiles; + } else if (filtername == "FormFiles") { + filter = SingleProjects.at(i).FormFiles; + } else if (filtername == "ResourceFiles") { + filter = SingleProjects.at(i).ResourceFiles; + } else { + // ExtraCompilers + filter = SingleProjects[i].filterForExtraCompiler(filtername); + } + + if (filter.Config) // only if the filter is not empty + filter.outputFileConfig(xml, info.file); + } + xml << closetag(_File); +} + +// outputs a given filter for all existing configurations of a project +void VCProject::outputFilter(XmlOutput &xml, +// VCProjectSingleConfig::FilterTypes type + const QString &filtername) +{ + Node *root; + if (SingleProjects.at(0).flat_files) + root = new FlatNode; + else + root = new TreeNode; + + QString name, extfilter, guid; + triState parse; + + for (int i = 0; i < SingleProjects.count(); ++i) { + VCFilter filter; + if (filtername == "RootFiles") { + filter = SingleProjects.at(i).RootFiles; + } else if (filtername == "Sources") { + filter = SingleProjects.at(i).SourceFiles; + } else if (filtername == "Headers") { + filter = SingleProjects.at(i).HeaderFiles; + } else if (filtername == "GeneratedFiles") { + filter = SingleProjects.at(i).GeneratedFiles; + } else if (filtername == "LexYaccFiles") { + filter = SingleProjects.at(i).LexYaccFiles; + } else if (filtername == "TranslationFiles") { + filter = SingleProjects.at(i).TranslationFiles; + } else if (filtername == "FormFiles") { + filter = SingleProjects.at(i).FormFiles; + } else if (filtername == "ResourceFiles") { + filter = SingleProjects.at(i).ResourceFiles; + } else { + // ExtraCompilers + filter = SingleProjects[i].filterForExtraCompiler(filtername); + } + + // Merge all files in this filter to root tree + for (int x = 0; x < filter.Files.count(); ++x) + root->addElement(filter.Files.at(x)); + + // Save filter setting from first filter. Next filters + // may differ but we cannot handle that. (ex. extfilter) + if (name.isEmpty()) { + name = filter.Name; + extfilter = filter.Filter; + parse = filter.ParseFiles; + guid = filter.Guid; + } + } + + if (!root->hasElements()) + return; + + // Actual XML output ---------------------------------- + if (!name.isEmpty()) { + xml << tag(_Filter) + << attrS(_Name, name) + << attrS(_Filter, extfilter) + << attrS(_UniqueIdentifier, guid) + << attrT(_ParseFiles, parse); + } + root->generateXML(xml, "", *this, filtername); // output root tree + if (!name.isEmpty()) + xml << closetag(_Filter); +} + +XmlOutput &operator<<(XmlOutput &xml, VCProject &tool) +{ + if (tool.SingleProjects.count() == 0) { + warn_msg(WarnLogic, "Generator: .NET: no single project in merge project, no output"); + return xml; + } + + xml << decl("1.0", "Windows-1252") + << tag(_VisualStudioProject) + << attrS(_ProjectType, "Visual C++") + << attrS(_Version, tool.Version) + << attrS(_Name, tool.Name) + << attrS(_ProjectGUID, tool.ProjectGUID) + << attrS(_Keyword, tool.Keyword) + << attrS(_SccProjectName, tool.SccProjectName) + << attrS(_SccLocalPath, tool.SccLocalPath) + << tag(_Platforms) + << tag(_Platform) + << attrS(_Name, tool.PlatformName) + << closetag(_Platforms) + << tag(_Configurations); + // Output each configuration + for (int i = 0; i < tool.SingleProjects.count(); ++i) + xml << tool.SingleProjects.at(i).Configuration; + xml << closetag(_Configurations) + << tag(_Files); + tool.outputFilter(xml, "Sources"); + tool.outputFilter(xml, "Headers"); + tool.outputFilter(xml, "GeneratedFiles"); + tool.outputFilter(xml, "LexYaccFiles"); + tool.outputFilter(xml, "TranslationFiles"); + tool.outputFilter(xml, "FormFiles"); + tool.outputFilter(xml, "ResourceFiles"); + for (int x = 0; x < tool.ExtraCompilers.count(); ++x) { + tool.outputFilter(xml, tool.ExtraCompilers.at(x)); + } + tool.outputFilter(xml, "RootFiles"); + xml << closetag(_Files) + << tag(_Globals) + << data(); // No "/>" end tag + return xml; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h new file mode 100644 index 0000000..e3f56f6 --- /dev/null +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -0,0 +1,1078 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_OBJECTMODEL_H +#define MSVC_OBJECTMODEL_H + +#include "project.h" +#include "xmloutput.h" +#include <qatomic.h> +#include <qlist.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qmap.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +enum DotNET { + NETUnknown = 0, + NET2002 = 0x70, + NET2003 = 0x71, + NET2005 = 0x80, + NET2008 = 0x90 +}; + +/* + This Object model is of course VERY simplyfied, + and does not actually follow the original MSVC + object model. However, it fulfilles the basic + needs for qmake +*/ + +/* + If a triState value is 'unset' then the + corresponding property is not in the output, + forcing the tool to utilize default values. + False/True values will be in the output... +*/ +enum customBuildCheck { + none, + mocSrc, + mocHdr, + lexyacc +}; +enum triState { + unset = -1, + _False = 0, + _True = 1 +}; +enum addressAwarenessType { + addrAwareDefault, + addrAwareNoLarge, + addrAwareLarge +}; +enum asmListingOption { + asmListingNone, + asmListingAssemblyOnly, + asmListingAsmMachineSrc, + asmListingAsmMachine, + asmListingAsmSrc +}; +enum basicRuntimeCheckOption { + runtimeBasicCheckNone, + runtimeCheckStackFrame, + runtimeCheckUninitVariables, + runtimeBasicCheckAll +}; +enum browseInfoOption { + brInfoNone, + brAllInfo, + brNoLocalSymbols +}; +enum callingConventionOption { + callConventionDefault = -1, + callConventionCDecl, + callConventionFastCall, + callConventionStdCall +}; +enum charSet { + charSetNotSet, + charSetUnicode, + charSetMBCS +}; +enum compileAsManagedOptions { + managedDefault = -1, // Was: noAssembly + managedAssembly = 1, + managedAssemblyPure = 2, // Old was: Assembly + managedAssemblySafe = 3, + managedAssemblyOldSyntax = 4 +}; +enum CompileAsOptions{ + compileAsDefault, + compileAsC, + compileAsCPlusPlus +}; +enum ConfigurationTypes { + typeUnknown = 0, + typeApplication = 1, + typeDynamicLibrary = 2, + typeStaticLibrary = 4, + typeGeneric = 10 +}; +enum debugOption { + debugUnknown = -1, + debugDisabled, + debugOldStyleInfo, + debugLineInfoOnly, + debugEnabled, + debugEditAndContinue +}; +enum eAppProtectionOption { + eAppProtectUnchanged, + eAppProtectLow, + eAppProtectMedium, + eAppProtectHigh +}; +enum enhancedInstructionSetOption { + archNotSet = 0, + archSSE = 1, + archSSE2 = 2 +}; +enum exceptionHandling { + ehDefault = -1, + ehNone = 0, + ehNoSEH = 1, + ehSEH = 2 +}; +enum enumResourceLangID { + rcUseDefault = 0, + rcAfrikaans = 1078, + rcAlbanian = 1052, + rcArabicAlgeria = 5121, + rcArabicBahrain = 15361, + rcArabicEgypt = 3073, + rcArabicIraq = 2049, + rcArabicJordan = 11265, + rcArabicKuwait = 13313, + rcArabicLebanon = 12289, + rcArabicLibya = 4097, + rcArabicMorocco = 6145, + rcArabicOman = 8193, + rcArabicQatar = 16385, + rcArabicSaudi = 1025, + rcArabicSyria = 10241, + rcArabicTunisia = 7169, + rcArabicUnitedArabEmirates = 14337, + rcArabicYemen = 9217, + rcBasque = 1069, + rcBulgarian = 1026, + rcByelorussian = 1059, + rcCatalan = 1027, + rcChineseHongKong = 3076, + rcChinesePRC = 2052, + rcChineseSingapore = 4100, + rcChineseTaiwan = 1028, + rcCroatian = 1050, + rcCzech = 1029, + rcDanish = 1030, + rcDutchBelgium = 2067, + rcDutchStandard = 1043, + rcEnglishAustralia = 3081, + rcEnglishBritain = 2057, + rcEnglishCanada = 4105, + RcEnglishCaribbean = 9225, + rcEnglishIreland = 6153, + rcEnglishJamaica = 8201, + rcEnglishNewZealand = 5129, + rcEnglishSouthAfrica = 7177, + rcEnglishUS = 1033, + rcEstonian = 1061, + rcFarsi = 1065, + rcFinnish = 1035, + rcFrenchBelgium = 2060, + rcFrenchCanada = 3084, + rcFrenchLuxembourg = 5132, + rcFrenchStandard = 1036, + rcFrenchSwitzerland = 4108, + rcGermanAustria = 3079, + rcGermanLichtenstein = 5127, + rcGermanLuxembourg = 4103, + rcGermanStandard = 1031, + rcGermanSwitzerland = 2055, + rcGreek = 1032, + rcHebrew = 1037, + rcHungarian = 1038, + rcIcelandic = 1039, + rcIndonesian = 1057, + rcItalianStandard = 1040, + rcItalianSwitzerland = 2064, + rcJapanese = 1041, + rcKorean = 1042, + rcKoreanJohab = 2066, + rcLatvian = 1062, + rcLithuanian = 1063, + rcNorwegianBokmal = 1044, + rcNorwegianNynorsk = 2068, + rcPolish = 1045, + rcPortugueseBrazilian = 1046, + rcPortugueseStandard = 2070, + rcRomanian = 1048, + rcRussian = 1049, + rcSerbian = 2074, + rcSlovak = 1051, + rcSpanishArgentina = 11274, + rcSpanishBolivia = 16394, + rcSpanishChile = 13322, + rcSpanishColombia = 9226, + rcSpanishCostaRica = 5130, + rcSpanishDominicanRepublic = 7178, + rcSpanishEcuador = 12298, + rcSpanishGuatemala = 4106, + rcSpanishMexico = 2058, + rcSpanishModern = 3082, + rcSpanishPanama = 6154, + rcSpanishParaguay = 15370, + rcSpanishPeru = 10250, + rcSpanishTraditional = 1034, + rcSpanishUruguay = 14346, + rcSpanishVenezuela = 8202, + rcSwedish = 1053, + rcThai = 1054, + rcTurkish = 1055, + rcUkrainian = 1058, + rcUrdu = 1056 +}; +enum enumSccEvent { + eProjectInScc, + ePreDirtyNotification +}; +enum favorSizeOrSpeedOption { + favorNone, + favorSpeed, + favorSize +}; +enum floatingPointModel { + floatingPointNotSet = -1, + floatingPointPrecise, + floatingPointStrict, + floatingPointFast +}; +enum genProxyLanguage { + genProxyNative, + genProxyManaged +}; +enum inlineExpansionOption { + expandDisable, + expandOnlyInline, + expandAnySuitable, + expandDefault // Not useful number, but stops the output +}; +enum linkIncrementalType { + linkIncrementalDefault, + linkIncrementalNo, + linkIncrementalYes +}; +enum linkProgressOption { + linkProgressNotSet, + linkProgressAll, + linkProgressLibs +}; +enum machineTypeOption { + machineNotSet, + machineX86 +}; +enum midlCharOption { + midlCharUnsigned, + midlCharSigned, + midlCharAscii7 +}; +enum midlErrorCheckOption { + midlEnableCustom, + midlDisableAll, + midlEnableAll +}; +enum midlStructMemberAlignOption { + midlAlignNotSet, + midlAlignSingleByte, + midlAlignTwoBytes, + midlAlignFourBytes, + midlAlignEightBytes, + midlAlignSixteenBytes +}; +enum midlTargetEnvironment { + midlTargetNotSet, + midlTargetWin32, + midlTargetWin64 +}; +enum midlWarningLevelOption { + midlWarningLevel_0, + midlWarningLevel_1, + midlWarningLevel_2, + midlWarningLevel_3, + midlWarningLevel_4 +}; +enum optFoldingType { + optFoldingDefault, + optNoFolding, + optFolding +}; +enum optimizeOption { + optimizeDisabled, + optimizeMinSpace, + optimizeMaxSpeed, + optimizeFull, + optimizeCustom, + optimizeDefault // Not useful number, but stops the output +}; +enum optRefType { + optReferencesDefault, + optNoReferences, + optReferences +}; +enum optWin98Type { + optWin98Default, + optWin98No, + optWin98Yes +}; +enum optLinkTimeCodeGenType { + optLTCGDefault, + optLTCGEnabled, + optLTCGInstrument, + optLTCGOptimize, + optLTCGUpdate +}; +enum pchOption { + pchNone, + pchCreateUsingSpecific, + pchGenerateAuto, + pchUseUsingSpecific +}; +enum preprocessOption { + preprocessUnknown = -1, + preprocessNo, + preprocessYes, + preprocessNoLineNumbers +}; +enum ProcessorOptimizeOption { + procOptimizeBlended, //GB + procOptimizePentium, //G5 + procOptimizePentiumProAndAbove, //G6 + procOptimizePentium4AndAbove //G7 +}; +enum RegisterDeployOption { + registerNo = 0, + registerYes +}; +enum RemoteDebuggerType { + DbgLocal, + DbgRemote, + DbgRemoteTCPIP +}; +enum runtimeLibraryOption { + rtUnknown = -1, + rtMultiThreaded, + rtMultiThreadedDebug, + rtMultiThreadedDLL, + rtMultiThreadedDebugDLL, + rtSingleThreaded, + rtSingleThreadedDebug +}; +enum structMemberAlignOption { + alignNotSet, + alignSingleByte, + alignTwoBytes, + alignFourBytes, + alignEightBytes, + alignSixteenBytes +}; +enum subSystemOption { + subSystemNotSet, + subSystemConsole, + subSystemWindows +}; +enum termSvrAwarenessType { + termSvrAwareDefault, + termSvrAwareNo, + termSvrAwareYes +}; +enum toolSetType { + toolSetUtility, + toolSetMakefile, + toolSetLinker, + toolSetLibrarian, + toolSetAll +}; +enum TypeOfDebugger { + DbgNativeOnly, + DbgManagedOnly, + DbgMixed, + DbgAuto +}; +enum useOfATL { + useATLNotSet, + useATLStatic, + useATLDynamic +}; +enum useOfMfc { + useMfcStdWin, + useMfcStatic, + useMfcDynamic +}; +enum useOfArchitecture { + archUnknown = -1, + archArmv4, + archArmv5, + archArmv4T, + archArmv5T, + archMips1 = 0, + archMips2 = 1, + archMips3 = 2, + archMips4 = 3, + archMips5 = 4, + archMips16 = 5, + archMips32 = 6, + archMips64 = 7 +}; +enum warningLevelOption { + warningLevelUnknown = -1, + warningLevel_0, + warningLevel_1, + warningLevel_2, + warningLevel_3, + warningLevel_4 +}; + + +class VCToolBase { +protected: + // Functions + VCToolBase(){}; + virtual ~VCToolBase(){} + virtual bool parseOption(const char* option) = 0; +public: + void parseOptions(QStringList& options) { + for (QStringList::ConstIterator it=options.begin(); (it!=options.end()); it++) + parseOption((*it).toLatin1()); + } +}; + +class VCConfiguration; +class VCProject; + +class VCCLCompilerTool : public VCToolBase +{ +public: + // Functions + VCCLCompilerTool(); + virtual ~VCCLCompilerTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + QStringList AdditionalUsingDirectories; + QString AssemblerListingLocation; + asmListingOption AssemblerOutput; + basicRuntimeCheckOption BasicRuntimeChecks; + browseInfoOption BrowseInformation; + QString BrowseInformationFile; + triState BufferSecurityCheck; + callingConventionOption CallingConvention; + CompileAsOptions CompileAs; + compileAsManagedOptions CompileAsManaged; + triState CompileOnly; + debugOption DebugInformationFormat; + triState DefaultCharIsUnsigned; + triState Detect64BitPortabilityProblems; + triState DisableLanguageExtensions; + QStringList DisableSpecificWarnings; + enhancedInstructionSetOption EnableEnhancedInstructionSet; + triState EnableFiberSafeOptimizations; + triState EnableFunctionLevelLinking; + triState EnableIntrinsicFunctions; + exceptionHandling ExceptionHandling; + triState ExpandAttributedSource; + favorSizeOrSpeedOption FavorSizeOrSpeed; + floatingPointModel FloatingPointModel; + triState FloatingPointExceptions; + triState ForceConformanceInForLoopScope; + QStringList ForcedIncludeFiles; + QStringList ForcedUsingFiles; + preprocessOption GeneratePreprocessedFile; + triState GlobalOptimizations; + triState IgnoreStandardIncludePath; + triState ImproveFloatingPointConsistency; + inlineExpansionOption InlineFunctionExpansion; + triState KeepComments; + triState MinimalRebuild; + QString ObjectFile; + triState OmitFramePointers; + triState OpenMP; + optimizeOption Optimization; + ProcessorOptimizeOption OptimizeForProcessor; + triState OptimizeForWindowsApplication; + QString OutputFile; + QString PrecompiledHeaderFile; + QString PrecompiledHeaderThrough; + QStringList PreprocessorDefinitions; + QString ProgramDataBaseFileName; + runtimeLibraryOption RuntimeLibrary; + triState RuntimeTypeInfo; + triState ShowIncludes; + triState SmallerTypeCheck; + triState StringPooling; + structMemberAlignOption StructMemberAlignment; + triState SuppressStartupBanner; + triState TreatWChar_tAsBuiltInType; + triState TurnOffAssemblyGeneration; + triState UndefineAllPreprocessorDefinitions; + QStringList UndefinePreprocessorDefinitions; + pchOption UsePrecompiledHeader; + triState WarnAsError; + warningLevelOption WarningLevel; + triState WholeProgramOptimization; + useOfArchitecture CompileForArchitecture; + triState InterworkCalls; + VCConfiguration* config; +}; + +class VCLinkerTool : public VCToolBase +{ +public: + // Functions + VCLinkerTool(); + virtual ~VCLinkerTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalDependencies; + QStringList AdditionalLibraryDirectories; + QStringList AdditionalOptions; + QStringList AddModuleNamesToAssembly; + QString BaseAddress; + QStringList DelayLoadDLLs; + optFoldingType EnableCOMDATFolding; + QString EntryPointSymbol; + QStringList ForceSymbolReferences; + QString FunctionOrder; + triState GenerateDebugInformation; + triState GenerateMapFile; + qlonglong HeapCommitSize; + qlonglong HeapReserveSize; + triState IgnoreAllDefaultLibraries; + QStringList IgnoreDefaultLibraryNames; + triState IgnoreEmbeddedIDL; + triState IgnoreImportLibrary; + QString ImportLibrary; + addressAwarenessType LargeAddressAware; + triState LinkDLL; + linkIncrementalType LinkIncremental; + optLinkTimeCodeGenType LinkTimeCodeGeneration; + QString LinkToManagedResourceFile; + triState MapExports; + QString MapFileName; + triState MapLines; + QString MergedIDLBaseFileName; + QString MergeSections; // Should be list? + QString MidlCommandFile; + QString ModuleDefinitionFile; // Should be list? + optWin98Type OptimizeForWindows98; + optRefType OptimizeReferences; + QString OutputFile; + QString ProgramDatabaseFile; + triState RegisterOutput; + triState ResourceOnlyDLL; + triState SetChecksum; + linkProgressOption ShowProgress; + qlonglong StackCommitSize; + qlonglong StackReserveSize; + QString StripPrivateSymbols; // Should be list? + subSystemOption SubSystem; + triState SupportUnloadOfDelayLoadedDLL; + triState SuppressStartupBanner; + triState SwapRunFromCD; + triState SwapRunFromNet; + machineTypeOption TargetMachine; + termSvrAwarenessType TerminalServerAware; + triState TurnOffAssemblyGeneration; + QString TypeLibraryFile; + qlonglong TypeLibraryResourceID; + QString Version; + VCConfiguration* config; +}; + +class VCMIDLTool : public VCToolBase +{ +public: + // Functions + VCMIDLTool(); + virtual ~VCMIDLTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + QStringList CPreprocessOptions; + midlCharOption DefaultCharType; + QString DLLDataFileName; // Should be list? + midlErrorCheckOption EnableErrorChecks; + triState ErrorCheckAllocations; + triState ErrorCheckBounds; + triState ErrorCheckEnumRange; + triState ErrorCheckRefPointers; + triState ErrorCheckStubData; + QStringList FullIncludePath; + triState GenerateStublessProxies; + triState GenerateTypeLibrary; + QString HeaderFileName; + triState IgnoreStandardIncludePath; + QString InterfaceIdentifierFileName; + triState MkTypLibCompatible; + QString OutputDirectory; + QStringList PreprocessorDefinitions; + QString ProxyFileName; + QString RedirectOutputAndErrors; + midlStructMemberAlignOption StructMemberAlignment; + triState SuppressStartupBanner; + midlTargetEnvironment TargetEnvironment; + QString TypeLibraryName; + QStringList UndefinePreprocessorDefinitions; + triState ValidateParameters; + triState WarnAsError; + midlWarningLevelOption WarningLevel; + VCConfiguration* config; +}; + +class VCLibrarianTool : public VCToolBase +{ +public: + // Functions + VCLibrarianTool(); + virtual ~VCLibrarianTool(){} + bool parseOption(const char*){ return false; }; + + // Variables + QStringList AdditionalDependencies; + QStringList AdditionalLibraryDirectories; + QStringList AdditionalOptions; + QStringList ExportNamedFunctions; + QStringList ForceSymbolReferences; + triState IgnoreAllDefaultLibraries; + QStringList IgnoreDefaultLibraryNames; + QString ModuleDefinitionFile; + QString OutputFile; + triState SuppressStartupBanner; +}; + +class VCCustomBuildTool : public VCToolBase +{ +public: + // Functions + VCCustomBuildTool(); + virtual ~VCCustomBuildTool(){} + bool parseOption(const char*){ return false; }; + + // Variables + QStringList AdditionalDependencies; + QStringList CommandLine; + QString Description; + QStringList Outputs; + QString ToolName; + QString ToolPath; +}; + +class VCResourceCompilerTool : public VCToolBase +{ +public: + // Functions + VCResourceCompilerTool(); + virtual ~VCResourceCompilerTool(){} + bool parseOption(const char*){ return false; }; + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + enumResourceLangID Culture; + QStringList FullIncludePath; + triState IgnoreStandardIncludePath; + QStringList PreprocessorDefinitions; + QString ResourceOutputFileName; + linkProgressOption ShowProgress; + QString ToolPath; +}; + +class VCDeploymentTool +{ +public: + // Functions + VCDeploymentTool(); + virtual ~VCDeploymentTool() {} + + // Variables + QString DeploymentTag; + QString RemoteDirectory; + RegisterDeployOption RegisterOutput; + QString AdditionalFiles; +}; + +class VCEventTool : public VCToolBase +{ +protected: + // Functions + VCEventTool() : ExcludedFromBuild(unset){}; + virtual ~VCEventTool(){} + bool parseOption(const char*){ return false; }; + +public: + // Variables + QString CommandLine; + QString Description; + triState ExcludedFromBuild; + QString ToolName; + QString ToolPath; +}; + +class VCPostBuildEventTool : public VCEventTool +{ +public: + VCPostBuildEventTool(); + ~VCPostBuildEventTool(){} +}; + +class VCPreBuildEventTool : public VCEventTool +{ +public: + VCPreBuildEventTool(); + ~VCPreBuildEventTool(){} +}; + +class VCPreLinkEventTool : public VCEventTool +{ +public: + VCPreLinkEventTool(); + ~VCPreLinkEventTool(){} +}; + +class VCConfiguration +{ +public: + // Functions + VCConfiguration(); + ~VCConfiguration(){} + + DotNET CompilerVersion; + + // Variables + triState ATLMinimizesCRunTimeLibraryUsage; + triState BuildBrowserInformation; + charSet CharacterSet; + ConfigurationTypes ConfigurationType; + QString DeleteExtensionsOnClean; + QString ImportLibrary; + QString IntermediateDirectory; + QString Name; + QString OutputDirectory; + QString PrimaryOutput; + QString ProgramDatabase; + triState RegisterOutput; + useOfATL UseOfATL; + useOfMfc UseOfMfc; + triState WholeProgramOptimization; + + // XML sub-parts + VCCLCompilerTool compiler; + VCLinkerTool linker; + VCLibrarianTool librarian; + VCCustomBuildTool custom; + VCMIDLTool idl; + VCPostBuildEventTool postBuild; + VCPreBuildEventTool preBuild; + VCDeploymentTool deployment; + VCPreLinkEventTool preLink; + VCResourceCompilerTool resource; +}; + +struct VCFilterFile +{ + VCFilterFile() + { excludeFromBuild = false; } + VCFilterFile(const QString &filename, bool exclude = false ) + { file = filename; excludeFromBuild = exclude; } + VCFilterFile(const QString &filename, const QString &additional, bool exclude = false ) + { file = filename; excludeFromBuild = exclude; additionalFile = additional; } + bool operator==(const VCFilterFile &other){ + return file == other.file + && additionalFile == other.additionalFile + && excludeFromBuild == other.excludeFromBuild; + } + + bool excludeFromBuild; + QString file; + QString additionalFile; // For tools like MOC +}; + +#ifndef QT_NO_DEBUG_OUTPUT +inline QDebug operator<<(QDebug dbg, const VCFilterFile &p) +{ + dbg.nospace() << "VCFilterFile(file(" << p.file + << ") additionalFile(" << p.additionalFile + << ") excludeFromBuild(" << p.excludeFromBuild << "))" << endl; + return dbg.space(); +} +#endif + +class VcprojGenerator; +class VCFilter +{ +public: + // Functions + VCFilter(); + ~VCFilter(){}; + + void addFile(const QString& filename); + void addFile(const VCFilterFile& fileInfo); + void addFiles(const QStringList& fileList); + bool addExtraCompiler(const VCFilterFile &info); + void modifyPCHstage(QString str); + void outputFileConfig(XmlOutput &xml, const QString &filename); + + // Variables + QString Name; + QString Filter; + QString Guid; + triState ParseFiles; + VcprojGenerator* Project; + VCConfiguration* Config; + QList<VCFilterFile> Files; + + customBuildCheck CustomBuild; + + bool useCustomBuildTool; + VCCustomBuildTool CustomBuildTool; + + bool useCompilerTool; + VCCLCompilerTool CompilerTool; + +private: + friend XmlOutput &operator<<(XmlOutput &xml, VCFilter &tool); +}; + +typedef QList<VCFilter> VCFilterList; +class VCProjectSingleConfig +{ +public: + enum FilterTypes { + None, + Source, + Header, + Generated, + LexYacc, + Translation, + Resources, + Extras + }; + // Functions + VCProjectSingleConfig(){}; + ~VCProjectSingleConfig(){} + + // Variables + QString Name; + QString Version; + QString ProjectGUID; + QString Keyword; + QString SccProjectName; + QString SccLocalPath; + QString PlatformName; + + // XML sub-parts + VCConfiguration Configuration; + VCFilter RootFiles; + VCFilter SourceFiles; + VCFilter HeaderFiles; + VCFilter GeneratedFiles; + VCFilter LexYaccFiles; + VCFilter TranslationFiles; + VCFilter FormFiles; + VCFilter ResourceFiles; + VCFilterList ExtraCompilersFiles; + + bool flat_files; + + // Accessor for extracompilers + VCFilter &filterForExtraCompiler(const QString &compilerName); +}; + + + +// Tree & Flat view of files -------------------------------------------------- +class VCFilter; +class Node +{ +public: + virtual ~Node() { } + void addElement(const VCFilterFile &file) { + addElement(file.file, file); + } + virtual void addElement(const QString &filepath, const VCFilterFile &allInfo) = 0; + virtual void removeElements()= 0; + virtual void generateXML(XmlOutput &xml, const QString &tagName, VCProject &tool, const QString &filter) = 0; + virtual bool hasElements() = 0; +}; + +class TreeNode : public Node +{ + typedef QMap<QString, TreeNode*> ChildrenMap; + VCFilterFile info; + ChildrenMap children; + +public: + virtual ~TreeNode() { removeElements(); } + + int pathIndex(const QString &filepath) { + int Windex = filepath.indexOf("\\"); + int Uindex = filepath.indexOf("/"); + if (Windex != -1 && Uindex != -1) + return qMin(Windex, Uindex); + else if (Windex != -1) + return Windex; + return Uindex; + } + + void addElement(const QString &filepath, const VCFilterFile &allInfo){ + QString newNodeName(filepath); + + int index = pathIndex(filepath); + if (index != -1) + newNodeName = filepath.left(index); + + TreeNode *n = children.value(newNodeName); + if (!n) { + n = new TreeNode; + n->info = allInfo; + children.insert(newNodeName, n); + } + if (index != -1) + n->addElement(filepath.mid(index+1), allInfo); + } + + void removeElements() { + ChildrenMap::ConstIterator it = children.constBegin(); + ChildrenMap::ConstIterator end = children.constEnd(); + for( ; it != end; it++) { + (*it)->removeElements(); + delete it.value(); + } + children.clear(); + } + + void generateXML(XmlOutput &xml, const QString &tagName, VCProject &tool, const QString &filter); + bool hasElements() { + return children.size() != 0; + } +}; + +class FlatNode : public Node +{ + typedef QMap<QString, VCFilterFile> ChildrenMapFlat; + ChildrenMapFlat children; + +public: + virtual ~FlatNode() { removeElements(); } + + int pathIndex(const QString &filepath) { + int Windex = filepath.lastIndexOf("\\"); + int Uindex = filepath.lastIndexOf("/"); + if (Windex != -1 && Uindex != -1) + return qMax(Windex, Uindex); + else if (Windex != -1) + return Windex; + return Uindex; + } + + void addElement(const QString &filepath, const VCFilterFile &allInfo){ + QString newKey(filepath); + + int index = pathIndex(filepath); + if (index != -1) + newKey = filepath.mid(index+1); + + // Key designed to sort files with same + // name in different paths correctly + children.insert(newKey + "\0" + allInfo.file, allInfo); + } + + void removeElements() { + children.clear(); + } + + void generateXML(XmlOutput &xml, const QString &tagName, VCProject &proj, const QString &filter); + bool hasElements() { + return children.size() != 0; + } +}; +// ---------------------------------------------------------------------------- + +class VCProject +{ +public: + // Variables + QString Name; + QString Version; + QString ProjectGUID; + QString Keyword; + QString SccProjectName; + QString SccLocalPath; + QString PlatformName; + + // Single projects + QList<VCProjectSingleConfig> SingleProjects; + + // List of all extracompilers + QStringList ExtraCompilers; + + // Functions + void outputFilter(XmlOutput &xml, +// VCProjectSingleConfig::FilterTypes type, + const QString &filtername); + + void outputFileConfigs(XmlOutput &xml, +// VCProjectSingleConfig::FilterTypes type, + const VCFilterFile &info, + const QString &filtername); +}; + +XmlOutput &operator<<(XmlOutput &, const VCCLCompilerTool &); +XmlOutput &operator<<(XmlOutput &, const VCLinkerTool &); +XmlOutput &operator<<(XmlOutput &, const VCMIDLTool &); +XmlOutput &operator<<(XmlOutput &, const VCCustomBuildTool &); +XmlOutput &operator<<(XmlOutput &, const VCLibrarianTool &); +XmlOutput &operator<<(XmlOutput &, const VCResourceCompilerTool &); +XmlOutput &operator<<(XmlOutput &, const VCEventTool &); +XmlOutput &operator<<(XmlOutput &, const VCDeploymentTool &); +XmlOutput &operator<<(XmlOutput &, const VCConfiguration &); +XmlOutput &operator<<(XmlOutput &, VCFilter &); +XmlOutput &operator<<(XmlOutput &, const VCProjectSingleConfig &); +XmlOutput &operator<<(XmlOutput &, VCProject &); + +QT_END_NAMESPACE + +#endif // MSVC_OBJECTMODEL_H diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp new file mode 100644 index 0000000..08159b0 --- /dev/null +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -0,0 +1,1785 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_vcproj.h" +#include "option.h" +#include "xmloutput.h" +#include <qdir.h> +#include <qdiriterator.h> +#include <qcryptographichash.h> +#include <qregexp.h> +#include <qhash.h> +#include <quuid.h> +#include <stdlib.h> + +//#define DEBUG_SOLUTION_GEN +//#define DEBUG_PROJECT_GEN + +QT_BEGIN_NAMESPACE +// Filter GUIDs (Do NOT change these!) ------------------------------ +const char _GUIDSourceFiles[] = "{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"; +const char _GUIDHeaderFiles[] = "{93995380-89BD-4b04-88EB-625FBE52EBFB}"; +const char _GUIDGeneratedFiles[] = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}"; +const char _GUIDResourceFiles[] = "{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}"; +const char _GUIDLexYaccFiles[] = "{E12AE0D2-192F-4d59-BD23-7D3FA58D3183}"; +const char _GUIDTranslationFiles[] = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}"; +const char _GUIDFormFiles[] = "{99349809-55BA-4b9d-BF79-8FDBB0286EB3}"; +const char _GUIDExtraCompilerFiles[] = "{E0D8C965-CC5F-43d7-AD63-FAEF0BBC0F85}"; +QT_END_NAMESPACE + +#ifdef Q_OS_WIN32 +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +struct { + DotNET version; + const char *versionStr; + const char *regKey; +} dotNetCombo[] = { +#ifdef Q_OS_WIN64 + {NET2008, "MSVC.NET 2008 (9.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 Express Edition (9.0)", "Software\\Wow6432Node\\Microsoft\\VCExpress\\9.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 (8.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 Express Edition (8.0)", "Software\\Wow6432Node\\Microsoft\\VCExpress\\8.0\\Setup\\VC\\ProductDir"}, + {NET2003, "MSVC.NET 2003 (7.1)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir"}, + {NET2002, "MSVC.NET 2002 (7.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir"}, +#else + {NET2008, "MSVC.NET 2008 (9.0)", "Software\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 Express Edition (9.0)", "Software\\Microsoft\\VCExpress\\9.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 (8.0)", "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 Express Edition (8.0)", "Software\\Microsoft\\VCExpress\\8.0\\Setup\\VC\\ProductDir"}, + {NET2003, "MSVC.NET 2003 (7.1)", "Software\\Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir"}, + {NET2002, "MSVC.NET 2002 (7.0)", "Software\\Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir"}, +#endif + {NETUnknown, "", ""}, +}; + +static QString keyPath(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return QString(); + return rKey.left(idx + 1); +} + +static QString keyName(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return rKey; + + QString res(rKey.mid(idx + 1)); + if (res == "Default" || res == ".") + res = ""; + return res; +} + +static QString readRegistryKey(HKEY parentHandle, const QString &rSubkey) +{ + + QString rSubkeyName = keyName(rSubkey); + QString rSubkeyPath = keyPath(rSubkey); + + HKEY handle = 0; + LONG res; + QT_WA( { + res = RegOpenKeyExW(parentHandle, (WCHAR*)rSubkeyPath.utf16(), + 0, KEY_READ, &handle); + } , { + res = RegOpenKeyExA(parentHandle, rSubkeyPath.toLocal8Bit(), + 0, KEY_READ, &handle); + } ); + + if (res != ERROR_SUCCESS) + return QString(); + + // get the size and type of the value + DWORD dataType; + DWORD dataSize; + QT_WA( { + res = RegQueryValueExW(handle, (WCHAR*)rSubkeyName.utf16(), 0, &dataType, 0, &dataSize); + }, { + res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, &dataType, 0, &dataSize); + } ); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + // get the value + QByteArray data(dataSize, 0); + QT_WA( { + res = RegQueryValueExW(handle, (WCHAR*)rSubkeyName.utf16(), 0, 0, + reinterpret_cast<unsigned char*>(data.data()), &dataSize); + }, { + res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, 0, + reinterpret_cast<unsigned char*>(data.data()), &dataSize); + } ); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + QString result; + switch (dataType) { + case REG_EXPAND_SZ: + case REG_SZ: { + QT_WA( { + result = QString::fromUtf16(((const ushort*)data.constData())); + }, { + result = QString::fromLatin1(data.constData()); + } ); + break; + } + + case REG_MULTI_SZ: { + QStringList l; + int i = 0; + for (;;) { + QString s; + QT_WA( { + s = QString::fromUtf16((const ushort*)data.constData() + i); + }, { + s = QString::fromLatin1(data.constData() + i); + } ); + i += s.length() + 1; + + if (s.isEmpty()) + break; + l.append(s); + } + result = l.join(", "); + break; + } + + case REG_NONE: + case REG_BINARY: { + QT_WA( { + result = QString::fromUtf16((const ushort*)data.constData(), data.size()/2); + }, { + result = QString::fromLatin1(data.constData(), data.size()); + } ); + break; + } + + case REG_DWORD_BIG_ENDIAN: + case REG_DWORD: { + Q_ASSERT(data.size() == sizeof(int)); + int i; + memcpy((char*)&i, data.constData(), sizeof(int)); + result = QString::number(i); + break; + } + + default: + qWarning("QSettings: unknown data %d type in windows registry", dataType); + break; + } + + RegCloseKey(handle); + return result; +} +QT_END_NAMESPACE +#endif + +QT_BEGIN_NAMESPACE +DotNET which_dotnet_version() +{ +#ifndef Q_OS_WIN32 + return NET2002; // Always generate 7.0 versions on other platforms +#else + // Only search for the version once + static DotNET current_version = NETUnknown; + if(current_version != NETUnknown) + return current_version; + + // Fallback to .NET 2002 + current_version = NET2002; + + QStringList warnPath; + int installed = 0; + int i = 0; + for(; dotNetCombo[i].version; ++i) { + QString path = readRegistryKey(HKEY_LOCAL_MACHINE, dotNetCombo[i].regKey); + if(!path.isEmpty()) { + ++installed; + current_version = dotNetCombo[i].version; + warnPath += QString("%1").arg(dotNetCombo[i].versionStr); + } + } + + if (installed < 2) + return current_version; + + // More than one version installed, search directory path + QString paths = qgetenv("PATH"); + QStringList pathlist = paths.toLower().split(";"); + + i = installed = 0; + for(; dotNetCombo[i].version; ++i) { + QString productPath = readRegistryKey(HKEY_LOCAL_MACHINE, dotNetCombo[i].regKey).toLower(); + if (productPath.isEmpty()) + continue; + QStringList::iterator it; + for(it = pathlist.begin(); it != pathlist.end(); ++it) { + if((*it).contains(productPath)) { + ++installed; + current_version = dotNetCombo[i].version; + warnPath += QString("%1 in path").arg(dotNetCombo[i].versionStr); + break; + } + } + } + switch(installed) { + case 1: + break; + case 0: + warn_msg(WarnLogic, "Generator: MSVC.NET: Found more than one version of Visual Studio, but" + " none in your path! Fallback to lowest version (%s)", warnPath.join(", ").toLatin1().data()); + break; + default: + warn_msg(WarnLogic, "Generator: MSVC.NET: Found more than one version of Visual Studio in" + " your path! Fallback to lowest version (%s)", warnPath.join(", ").toLatin1().data()); + break; + } + + return current_version; +#endif +}; + +// Flatfile Tags ---------------------------------------------------- +const char _slnHeader70[] = "Microsoft Visual Studio Solution File, Format Version 7.00"; +const char _slnHeader71[] = "Microsoft Visual Studio Solution File, Format Version 8.00"; +const char _slnHeader80[] = "Microsoft Visual Studio Solution File, Format Version 9.00"; +const char _slnHeader90[] = "Microsoft Visual Studio Solution File, Format Version 10.00"; + // The following UUID _may_ change for later servicepacks... + // If so we need to search through the registry at + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\Projects + // to find the subkey that contains a "PossibleProjectExtension" + // containing "vcproj"... + // Use the hardcoded value for now so projects generated on other + // platforms are actually usable. +const char _slnMSVCvcprojGUID[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; +const char _slnProjectBeg[] = "\nProject(\""; +const char _slnProjectMid[] = "\") = "; +const char _slnProjectEnd[] = "\nEndProject"; +const char _slnGlobalBeg[] = "\nGlobal"; +const char _slnGlobalEnd[] = "\nEndGlobal"; +const char _slnSolutionConf[] = "\n\tGlobalSection(SolutionConfiguration) = preSolution" + "\n\t\tConfigName.0 = Debug|Win32" + "\n\t\tConfigName.1 = Release|Win32" + "\n\tEndGlobalSection"; +const char _slnProjDepBeg[] = "\n\tGlobalSection(ProjectDependencies) = postSolution"; +const char _slnProjDepEnd[] = "\n\tEndGlobalSection"; +const char _slnProjConfBeg[] = "\n\tGlobalSection(ProjectConfiguration) = postSolution"; +const char _slnProjRelConfTag1[]= ".Release|%1.ActiveCfg = Release|"; +const char _slnProjRelConfTag2[]= ".Release|%1.Build.0 = Release|"; +const char _slnProjDbgConfTag1[]= ".Debug|%1.ActiveCfg = Debug|"; +const char _slnProjDbgConfTag2[]= ".Debug|%1.Build.0 = Debug|"; +const char _slnProjConfEnd[] = "\n\tEndGlobalSection"; +const char _slnExtSections[] = "\n\tGlobalSection(ExtensibilityGlobals) = postSolution" + "\n\tEndGlobalSection" + "\n\tGlobalSection(ExtensibilityAddIns) = postSolution" + "\n\tEndGlobalSection"; +// ------------------------------------------------------------------ + +VcprojGenerator::VcprojGenerator() : Win32MakefileGenerator(), init_flag(false) +{ +} +bool VcprojGenerator::writeMakefile(QTextStream &t) +{ + initProject(); // Fills the whole project with proper data + + // Generate solution file + if(project->first("TEMPLATE") == "vcsubdirs") { + if (!project->isActiveConfig("build_pass")) { + debug_msg(1, "Generator: MSVC.NET: Writing solution file"); + writeSubDirs(t); + } else { + debug_msg(1, "Generator: MSVC.NET: Not writing solution file for build_pass configs"); + } + return true; + } else + // Generate single configuration project file + if (project->first("TEMPLATE") == "vcapp" || + project->first("TEMPLATE") == "vclib") { + if(!project->isActiveConfig("build_pass")) { + debug_msg(1, "Generator: MSVC.NET: Writing single configuration project file"); + XmlOutput xmlOut(t); + xmlOut << vcProject; + } + return true; + } + return project->isActiveConfig("build_pass"); +} + +bool VcprojGenerator::writeProjectMakefile() +{ + usePlatformDir(); + QTextStream t(&Option::output); + + // Check if all requirements are fulfilled + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return true; + } + + // Generate project file + if(project->first("TEMPLATE") == "vcapp" || + project->first("TEMPLATE") == "vclib") { + if (!mergedProjects.count()) { + warn_msg(WarnLogic, "Generator: MSVC.NET: no single configuration created, cannot output project!"); + return false; + } + + debug_msg(1, "Generator: MSVC.NET: Writing project file"); + VCProject mergedProject; + for (int i = 0; i < mergedProjects.count(); ++i) { + VCProjectSingleConfig *singleProject = &(mergedProjects.at(i)->vcProject); + mergedProject.SingleProjects += *singleProject; + for (int j = 0; j < singleProject->ExtraCompilersFiles.count(); ++j) { + const QString &compilerName = singleProject->ExtraCompilersFiles.at(j).Name; + if (!mergedProject.ExtraCompilers.contains(compilerName)) + mergedProject.ExtraCompilers += compilerName; + } + } + + if(mergedProjects.count() > 1 && + mergedProjects.at(0)->vcProject.Name == + mergedProjects.at(1)->vcProject.Name) + mergedProjects.at(0)->writePrlFile(); + mergedProject.Name = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + mergedProject.Version = mergedProjects.at(0)->vcProject.Version; + mergedProject.ProjectGUID = project->isEmpty("QMAKE_UUID") ? getProjectUUID().toString().toUpper() : project->first("QMAKE_UUID"); + mergedProject.Keyword = project->first("VCPROJ_KEYWORD"); + mergedProject.SccProjectName = mergedProjects.at(0)->vcProject.SccProjectName; + mergedProject.SccLocalPath = mergedProjects.at(0)->vcProject.SccLocalPath; + mergedProject.PlatformName = mergedProjects.at(0)->vcProject.PlatformName; + + XmlOutput xmlOut(t); + xmlOut << mergedProject; + return true; + } else if(project->first("TEMPLATE") == "vcsubdirs") { + return writeMakefile(t); + } + return false; +} + +struct VcsolutionDepend { + QString uuid; + QString vcprojFile, orig_target, target; + Target targetType; + QStringList dependencies; +}; + +QUuid VcprojGenerator::getProjectUUID(const QString &filename) +{ + bool validUUID = true; + + // Read GUID from variable-space + QUuid uuid = project->first("GUID"); + + // If none, create one based on the MD5 of absolute project path + if(uuid.isNull() || !filename.isEmpty()) { + QString abspath = Option::fixPathToLocalOS(filename.isEmpty()?project->first("QMAKE_MAKEFILE"):filename); + QByteArray digest = QCryptographicHash::hash(abspath.toUtf8(), QCryptographicHash::Md5); + memcpy((unsigned char*)(&uuid), digest.constData(), sizeof(QUuid)); + validUUID = !uuid.isNull(); + uuid.data4[0] = (uuid.data4[0] & 0x3F) | 0x80; // UV_DCE variant + uuid.data3 = (uuid.data3 & 0x0FFF) | (QUuid::Name<<12); + } + + // If still not valid, generate new one, and suggest adding to .pro + if(uuid.isNull() || !validUUID) { + uuid = QUuid::createUuid(); + fprintf(stderr, + "qmake couldn't create a GUID based on filepath, and we couldn't\nfind a valid GUID in the .pro file (Consider adding\n'GUID = %s' to the .pro file)\n", + uuid.toString().toUpper().toLatin1().constData()); + } + + // Store GUID in variable-space + project->values("GUID") = QStringList(uuid.toString().toUpper()); + return uuid; +} + +QUuid VcprojGenerator::increaseUUID(const QUuid &id) +{ + QUuid result(id); + qint64 dataFirst = (result.data4[0] << 24) + + (result.data4[1] << 16) + + (result.data4[2] << 8) + + result.data4[3]; + qint64 dataLast = (result.data4[4] << 24) + + (result.data4[5] << 16) + + (result.data4[6] << 8) + + result.data4[7]; + + if(!(dataLast++)) + dataFirst++; + + result.data4[0] = uchar((dataFirst >> 24) & 0xff); + result.data4[1] = uchar((dataFirst >> 16) & 0xff); + result.data4[2] = uchar((dataFirst >> 8) & 0xff); + result.data4[3] = uchar(dataFirst & 0xff); + result.data4[4] = uchar((dataLast >> 24) & 0xff); + result.data4[5] = uchar((dataLast >> 16) & 0xff); + result.data4[6] = uchar((dataLast >> 8) & 0xff); + result.data4[7] = uchar(dataLast & 0xff); + return result; +} + +void VcprojGenerator::writeSubDirs(QTextStream &t) +{ + // Check if all requirements are fulfilled + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return; + } + + switch(which_dotnet_version()) { + case NET2008: + t << _slnHeader90; + break; + case NET2005: + t << _slnHeader80; + break; + case NET2003: + t << _slnHeader71; + break; + case NET2002: + t << _slnHeader70; + break; + default: + t << _slnHeader70; + warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .sln", which_dotnet_version()); + break; + } + + QHash<QString, VcsolutionDepend*> solution_depends; + QList<VcsolutionDepend*> solution_cleanup; + + QStringList subdirs = project->values("SUBDIRS"); + QString oldpwd = qmake_getpwd(); + + // Make sure that all temp projects are configured + // for release so that the depends are created + // without the debug <lib>dxxx.lib name mangling + QStringList old_after_vars = Option::after_user_vars; + Option::after_user_vars.append("CONFIG+=release"); + + for(int i = 0; i < subdirs.size(); ++i) { + QString tmp = subdirs.at(i); + if(!project->isEmpty(tmp + ".file")) { + if(!project->isEmpty(tmp + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + tmp.toLatin1().constData()); + tmp = project->first(tmp + ".file"); + } else if(!project->isEmpty(tmp + ".subdir")) { + tmp = project->first(tmp + ".subdir"); + } + 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() + ".pro"; + subdirs.append(profile); + } else { + 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)) { + // Check if all requirements are fulfilled + if(!tmp_proj.variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { + fprintf(stderr, "Project file(%s) not added to Solution because all requirements not met:\n\t%s\n", + fn.toLatin1().constData(), tmp_proj.values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData()); + continue; + } + if(tmp_proj.first("TEMPLATE") == "vcsubdirs") { + QStringList tmp_proj_subdirs = tmp_proj.variables()["SUBDIRS"]; + for(int x = 0; x < tmp_proj_subdirs.size(); ++x) { + QString tmpdir = tmp_proj_subdirs.at(x); + if(!tmp_proj.isEmpty(tmpdir + ".file")) { + if(!tmp_proj.isEmpty(tmpdir + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + tmpdir.toLatin1().constData()); + tmpdir = tmp_proj.first(tmpdir + ".file"); + } else if(!tmp_proj.isEmpty(tmpdir + ".subdir")) { + tmpdir = tmp_proj.first(tmpdir + ".subdir"); + } + subdirs += fileFixify(tmpdir); + } + } else if(tmp_proj.first("TEMPLATE") == "vcapp" || tmp_proj.first("TEMPLATE") == "vclib") { + // Initialize a 'fake' project to get the correct variables + // and to be able to extract all the dependencies + Option::QMAKE_MODE old_mode = Option::qmake_mode; + Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING; + VcprojGenerator tmp_vcproj; + tmp_vcproj.setNoIO(true); + tmp_vcproj.setProjectFile(&tmp_proj); + Option::qmake_mode = old_mode; + if(Option::debug_level) { + 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()); + } + } + + // We assume project filename is [QMAKE_ORIG_TARGET].vcproj + QString vcproj = unescapeFilePath(fixFilename(tmp_vcproj.project->first("QMAKE_ORIG_TARGET")) + project->first("VCPROJ_EXTENSION")); + QString vcprojDir = qmake_getpwd(); + + // If file doesn't exsist, then maybe the users configuration + // doesn't allow it to be created. Skip to next... + if(!exists(vcprojDir + Option::dir_sep + vcproj)) { + + // Try to find the directory which fits relative + // to the output path, which represents the shadow + // path in case we are shadow building + QStringList list = fi.path().split(QLatin1Char('/')); + QString tmpDir = QFileInfo(Option::output).path() + Option::dir_sep; + bool found = false; + for (int i = list.size() - 1; i >= 0; --i) { + QString curr; + for (int j = i; j < list.size(); ++j) + curr += list.at(j) + Option::dir_sep; + if (exists(tmpDir + curr + vcproj)) { + vcprojDir = QDir::cleanPath(tmpDir + curr); + found = true; + break; + } + } + if (!found) { + warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(vcprojDir + Option::dir_sep + vcproj).toLatin1().constData()); + goto nextfile; // # Dirty! + } + } + + VcsolutionDepend *newDep = new VcsolutionDepend; + newDep->vcprojFile = vcprojDir + Option::dir_sep + vcproj; + newDep->orig_target = unescapeFilePath(tmp_proj.first("QMAKE_ORIG_TARGET")); + newDep->target = tmp_proj.first("MSVCPROJ_TARGET").section(Option::dir_sep, -1); + newDep->targetType = tmp_vcproj.projectTarget; + newDep->uuid = tmp_proj.isEmpty("QMAKE_UUID") ? getProjectUUID(Option::fixPathToLocalOS(vcprojDir + QDir::separator() + vcproj)).toString().toUpper(): tmp_proj.first("QMAKE_UUID"); + + // We want to store it as the .lib name. + if(newDep->target.endsWith(".dll")) + newDep->target = newDep->target.left(newDep->target.length()-3) + "lib"; + + // All ActiveQt Server projects are dependent on idc.exe + if(tmp_proj.variables()["CONFIG"].contains("qaxserver")) + newDep->dependencies << "idc.exe"; + + // All extra compilers which has valid input are considered dependencies + const QStringList &quc = tmp_proj.variables()["QMAKE_EXTRA_COMPILERS"]; + for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) { + const QStringList &invar = tmp_proj.variables().value((*it) + ".input"); + for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { + const QStringList fileList = tmp_proj.variables().value(*iit); + if (!fileList.isEmpty()) { + const QStringList &cmdsParts = tmp_proj.variables().value((*it) + ".commands"); + bool startOfLine = true; + foreach(QString cmd, cmdsParts) { + if (!startOfLine) { + if (cmd.contains("\r")) + startOfLine = true; + continue; + } + if (cmd.isEmpty()) + continue; + + startOfLine = false; + // Extra compiler commands might be defined in variables, so + // expand them (don't care about the in/out files) + cmd = tmp_vcproj.replaceExtraCompilerVariables(cmd, QStringList(), QStringList()); + // Pull out command based on spaces and quoting, if the + // command starts with that + cmd = cmd.left(cmd.indexOf(cmd.at(0) == '"' ? '"' : ' ', 1)); + QString dep = cmd.section('/', -1).section('\\', -1); + if (!newDep->dependencies.contains(dep)) + newDep->dependencies << dep; + } + } + } + } + + // Add all unknown libs to the deps + QStringList where("QMAKE_LIBS"); + if(!tmp_proj.isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = tmp_proj.variables()["QMAKE_INTERNAL_PRL_LIBS"]; + for(QStringList::iterator wit = where.begin(); + wit != where.end(); ++wit) { + QStringList &l = tmp_proj.variables()[(*wit)]; + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString opt = (*it); + if(!opt.startsWith("/") && // Not a switch + opt != newDep->target && // Not self + opt != "opengl32.lib" && // We don't care about these libs + opt != "glu32.lib" && // to make depgen alittle faster + opt != "kernel32.lib" && + opt != "user32.lib" && + opt != "gdi32.lib" && + opt != "comdlg32.lib" && + opt != "advapi32.lib" && + opt != "shell32.lib" && + opt != "ole32.lib" && + opt != "oleaut32.lib" && + opt != "uuid.lib" && + opt != "imm32.lib" && + opt != "winmm.lib" && + opt != "wsock32.lib" && + opt != "ws2_32.lib" && + opt != "winspool.lib" && + opt != "delayimp.lib") + { + newDep->dependencies << opt.section(Option::dir_sep, -1); + } + } + } +#ifdef DEBUG_SOLUTION_GEN + qDebug("Deps for %20s: [%s]", newDep->target.toLatin1().constData(), newDep->dependencies.join(" :: ").toLatin1().constData()); +#endif + solution_cleanup.append(newDep); + solution_depends.insert(newDep->target, newDep); + t << _slnProjectBeg << _slnMSVCvcprojGUID << _slnProjectMid + << "\"" << newDep->orig_target << "\", \"" << newDep->vcprojFile + << "\", \"" << newDep->uuid << "\""; + t << _slnProjectEnd; + } + } +nextfile: + qmake_setpwd(oldpwd); + } + } + } + t << _slnGlobalBeg; + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) { + QString slnConfCE = _slnSolutionConf; + QString platform = QString("|") + project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + slnConfCE.replace(QString("|Win32"), platform); + t << slnConfCE; + } else { + t << _slnSolutionConf; + } + t << _slnProjDepBeg; + + // Restore previous after_user_var options + Option::after_user_vars = old_after_vars; + + // Figure out dependencies + for(QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { + int cnt = 0; + for(QStringList::iterator dit = (*it)->dependencies.begin(); dit != (*it)->dependencies.end(); ++dit) { + if(VcsolutionDepend *vc = solution_depends[*dit]) + t << "\n\t\t" << (*it)->uuid << "." << cnt++ << " = " << vc->uuid; + } + } + t << _slnProjDepEnd; + t << _slnProjConfBeg; + for(QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { + QString platform = "Win32"; + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) + platform = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag1).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag2).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag1).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag2).arg(platform) << platform; + } + t << _slnProjConfEnd; + t << _slnExtSections; + t << _slnGlobalEnd; + + + while (!solution_cleanup.isEmpty()) + delete solution_cleanup.takeFirst(); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ + +bool VcprojGenerator::hasBuiltinCompiler(const QString &file) +{ + // Source files + for (int i = 0; i < Option::cpp_ext.count(); ++i) + if (file.endsWith(Option::cpp_ext.at(i))) + return true; + for (int i = 0; i < Option::c_ext.count(); ++i) + if (file.endsWith(Option::c_ext.at(i))) + return true; + if (file.endsWith(".rc") + || file.endsWith(".idl")) + return true; + return false; +} + +void VcprojGenerator::init() +{ + if(init_flag) + return; + if(project->first("TEMPLATE") == "vcsubdirs") { //too much work for subdirs + init_flag = true; + return; + } + + debug_msg(1, "Generator: MSVC.NET: Initializing variables"); + + // this should probably not be here, but I'm using it to wrap the .t files + if (project->first("TEMPLATE") == "vcapp") + project->values("QMAKE_APP_FLAG").append("1"); + else if (project->first("TEMPLATE") == "vclib") + project->values("QMAKE_LIB_FLAG").append("1"); + if (project->values("QMAKESPEC").isEmpty()) + project->values("QMAKESPEC").append(qgetenv("QMAKESPEC")); + + processVars(); + initOld(); // Currently calling old DSP code to set variables. CLEAN UP! + + // Figure out what we're trying to build + if(project->first("TEMPLATE") == "vcapp") { + projectTarget = Application; + } else if(project->first("TEMPLATE") == "vclib") { + if(project->isActiveConfig("staticlib")) { + if (!project->values("RES_FILE").isEmpty()) + project->values("MSVCPROJ_LIBS") += escapeFilePaths(project->values("RES_FILE")); + projectTarget = StaticLib; + } else + projectTarget = SharedLib; + } + + // Setup PCH variables + precompH = project->first("PRECOMPILED_HEADER"); + precompCPP = project->first("PRECOMPILED_SOURCE"); + usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); + if (usePCH) { + precompHFilename = fileInfo(precompH).fileName(); + // Created files + QString origTarget = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + precompObj = origTarget + Option::obj_ext; + precompPch = origTarget + ".pch"; + // Add PRECOMPILED_HEADER to HEADERS + if (!project->values("HEADERS").contains(precompH)) + project->values("HEADERS") += precompH; + // Return to variable pool + project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); + project->values("PRECOMPILED_PCH") = QStringList(precompPch); + + autogenPrecompCPP = precompCPP.isEmpty() && project->isActiveConfig("autogen_precompile_source"); + if (autogenPrecompCPP) { + precompCPP = precompH + + (Option::cpp_ext.count() ? Option::cpp_ext.at(0) : QLatin1String(".cpp")); + project->values("GENERATED_SOURCES") += precompCPP; + } else if (!precompCPP.isEmpty()) { + project->values("SOURCES") += precompCPP; + } + } + + // Add all input files for a custom compiler into a map for uniqueness, + // unless the compiler is configure as a combined stage, then use the first one + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) { + const QStringList &invar = project->variables().value((*it) + ".input"); + const QString compiler_out = project->first((*it) + ".output"); + for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { + QStringList fileList = project->variables().value(*iit); + if (!fileList.isEmpty()) { + if (project->values((*it) + ".CONFIG").indexOf("combine") != -1) + fileList = QStringList(fileList.first()); + for(QStringList::ConstIterator fit = fileList.constBegin(); fit != fileList.constEnd(); ++fit) { + QString file = (*fit); + if (verifyExtraCompiler((*it), file)) { + if (!hasBuiltinCompiler(file)) { + extraCompilerSources[file] += *it; + } else { + QString out = Option::fixPathToTargetOS(replaceExtraCompilerVariables( + compiler_out, file, QString()), false); + extraCompilerSources[out] += *it; + extraCompilerOutputs[out] = QStringList(file); // Can only have one + } + } + } + } + } + } + +#if 0 // Debugging + Q_FOREACH(QString aKey, extraCompilerSources.keys()) { + qDebug("Extracompilers for %s are (%s)", aKey.toLatin1().constData(), extraCompilerSources.value(aKey).join(", ").toLatin1().constData()); + } + Q_FOREACH(QString aKey, extraCompilerOutputs.keys()) { + qDebug("Object mapping for %s is (%s)", aKey.toLatin1().constData(), extraCompilerOutputs.value(aKey).join(", ").toLatin1().constData()); + } + qDebug(""); +#endif +} + +bool VcprojGenerator::mergeBuildProject(MakefileGenerator *other) +{ + VcprojGenerator *otherVC = static_cast<VcprojGenerator*>(other); + if (!otherVC) { + warn_msg(WarnLogic, "VcprojGenerator: Cannot merge other types of projects! (ignored)"); + return false; + } + mergedProjects += otherVC; + return true; +} + +void VcprojGenerator::initProject() +{ + // Initialize XML sub elements + // - Do this first since project elements may need + // - to know of certain configuration options + initConfiguration(); + initRootFiles(); + initSourceFiles(); + initHeaderFiles(); + initGeneratedFiles(); + initLexYaccFiles(); + initTranslationFiles(); + initFormFiles(); + initResourceFiles(); + initExtraCompilerOutputs(); + + // Own elements ----------------------------- + vcProject.Name = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + switch(which_dotnet_version()) { + case NET2008: + vcProject.Version = "9,00"; + break; + case NET2005: + //### using ',' because of a bug in 2005 B2 + //### VS uses '.' or ',' depending on the regional settings! Using ',' always works. + vcProject.Version = "8,00"; + break; + case NET2003: + vcProject.Version = "7.10"; + break; + case NET2002: + vcProject.Version = "7.00"; + break; + default: + vcProject.Version = "7.00"; + warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .vcproj", which_dotnet_version()); + break; + } + + vcProject.Keyword = project->first("VCPROJ_KEYWORD"); + if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + vcProject.PlatformName = (vcProject.Configuration.idl.TargetEnvironment == midlTargetWin64 ? "Win64" : "Win32"); + } else { + vcProject.PlatformName = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + } + // These are not used by Qt, but may be used by customers + vcProject.SccProjectName = project->first("SCCPROJECTNAME"); + vcProject.SccLocalPath = project->first("SCCLOCALPATH"); + vcProject.flat_files = project->isActiveConfig("flat"); +} + +void VcprojGenerator::initConfiguration() +{ + // Initialize XML sub elements + // - Do this first since main configuration elements may need + // - to know of certain compiler/linker options + VCConfiguration &conf = vcProject.Configuration; + conf.CompilerVersion = which_dotnet_version(); + + initCompilerTool(); + + // Only on configuration per build + bool isDebug = project->isActiveConfig("debug"); + + if(projectTarget == StaticLib) + initLibrarianTool(); + else { + conf.linker.GenerateDebugInformation = isDebug ? _True : _False; + initLinkerTool(); + } + initResourceTool(); + initIDLTool(); + + // Own elements ----------------------------- + QString temp = project->first("BuildBrowserInformation"); + switch (projectTarget) { + case SharedLib: + conf.ConfigurationType = typeDynamicLibrary; + break; + case StaticLib: + conf.ConfigurationType = typeStaticLibrary; + break; + case Application: + default: + conf.ConfigurationType = typeApplication; + break; + } + + conf.Name = project->values("BUILD_NAME").join(" "); + if (conf.Name.isEmpty()) + conf.Name = isDebug ? "Debug" : "Release"; + if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + conf.Name += (conf.idl.TargetEnvironment == midlTargetWin64 ? "|Win64" : "|Win32"); + } else { + conf.Name += "|" + project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + } + conf.ATLMinimizesCRunTimeLibraryUsage = (project->first("ATLMinimizesCRunTimeLibraryUsage").isEmpty() ? _False : _True); + conf.BuildBrowserInformation = triState(temp.isEmpty() ? (short)unset : temp.toShort()); + temp = project->first("CharacterSet"); + conf.CharacterSet = charSet(temp.isEmpty() ? (short)charSetNotSet : temp.toShort()); + conf.DeleteExtensionsOnClean = project->first("DeleteExtensionsOnClean"); + conf.ImportLibrary = conf.linker.ImportLibrary; + conf.IntermediateDirectory = project->first("OBJECTS_DIR"); + conf.OutputDirectory = "."; + conf.PrimaryOutput = project->first("PrimaryOutput"); + conf.WholeProgramOptimization = conf.compiler.WholeProgramOptimization; + temp = project->first("UseOfATL"); + if(!temp.isEmpty()) + conf.UseOfATL = useOfATL(temp.toShort()); + temp = project->first("UseOfMfc"); + if(!temp.isEmpty()) + conf.UseOfMfc = useOfMfc(temp.toShort()); + + // Configuration does not need parameters from + // these sub XML items; + initCustomBuildTool(); + initPreBuildEventTools(); + initPostBuildEventTools(); + // Only deploy for CE projects + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) + initDeploymentTool(); + initPreLinkEventTools(); + + // Set definite values in both configurations + if (isDebug) { + conf.compiler.PreprocessorDefinitions.removeAll("NDEBUG"); + } else { + conf.compiler.PreprocessorDefinitions += "NDEBUG"; + } + +} + +void VcprojGenerator::initCompilerTool() +{ + QString placement = project->first("OBJECTS_DIR"); + if(placement.isEmpty()) + placement = ".\\"; + + VCConfiguration &conf = vcProject.Configuration; + conf.compiler.AssemblerListingLocation = placement ; + conf.compiler.ProgramDataBaseFileName = ".\\" ; + conf.compiler.ObjectFile = placement ; + // PCH + if (usePCH) { + conf.compiler.UsePrecompiledHeader = pchUseUsingSpecific; + conf.compiler.PrecompiledHeaderFile = "$(IntDir)\\" + precompPch; + conf.compiler.PrecompiledHeaderThrough = project->first("PRECOMPILED_HEADER"); + conf.compiler.ForcedIncludeFiles = project->values("PRECOMPILED_HEADER"); + // Minimal build option triggers an Internal Compiler Error + // when used in conjunction with /FI and /Yu, so remove it + project->values("QMAKE_CFLAGS_DEBUG").removeAll("-Gm"); + project->values("QMAKE_CFLAGS_DEBUG").removeAll("/Gm"); + project->values("QMAKE_CXXFLAGS_DEBUG").removeAll("-Gm"); + project->values("QMAKE_CXXFLAGS_DEBUG").removeAll("/Gm"); + } + + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS")); + if(project->isActiveConfig("debug")){ + // Debug version + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS")); + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_DEBUG")); + if((projectTarget == Application) || (projectTarget == StaticLib)) + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_MT_DBG")); + else + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_MT_DLLDBG")); + } else { + // Release version + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS")); + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_RELEASE")); + conf.compiler.PreprocessorDefinitions += "QT_NO_DEBUG"; + conf.compiler.PreprocessorDefinitions += "NDEBUG"; + if((projectTarget == Application) || (projectTarget == StaticLib)) + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_MT")); + else + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_MT_DLL")); + } + + // Common for both release and debug + if(project->isActiveConfig("warn_off")) + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_WARN_OFF")); + else if(project->isActiveConfig("warn_on")) + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS_WARN_ON")); + if(project->isActiveConfig("windows")) + conf.compiler.PreprocessorDefinitions += project->values("MSVCPROJ_WINCONDEF"); + + // Can this be set for ALL configs? + // If so, use qmake.conf! + if(projectTarget == SharedLib) + conf.compiler.PreprocessorDefinitions += "_WINDOWS"; + + conf.compiler.PreprocessorDefinitions += project->values("DEFINES"); + conf.compiler.PreprocessorDefinitions += project->values("PRL_EXPORT_DEFINES"); + conf.compiler.parseOptions(project->values("MSVCPROJ_INCPATH")); +} + +void VcprojGenerator::initLibrarianTool() +{ + VCConfiguration &conf = vcProject.Configuration; + conf.librarian.OutputFile = project->first("DESTDIR"); + if(conf.librarian.OutputFile.isEmpty()) + conf.librarian.OutputFile = ".\\"; + + if(!conf.librarian.OutputFile.endsWith("\\")) + conf.librarian.OutputFile += '\\'; + + conf.librarian.OutputFile += project->first("MSVCPROJ_TARGET"); + conf.librarian.AdditionalOptions += project->values("QMAKE_LIBFLAGS"); +} + +void VcprojGenerator::initLinkerTool() +{ + findLibraries(); // Need to add the highest version of the libs + VCConfiguration &conf = vcProject.Configuration; + conf.linker.parseOptions(project->values("MSVCPROJ_LFLAGS")); + + foreach(QString libs, project->values("MSVCPROJ_LIBS")) { + if (libs.left(9).toUpper() == "/LIBPATH:") { + QStringList l = QStringList(libs); + conf.linker.parseOptions(l); + } else { + conf.linker.AdditionalDependencies += libs; + } + } + + switch (projectTarget) { + case Application: + conf.linker.OutputFile = project->first("DESTDIR"); + break; + case SharedLib: + conf.linker.parseOptions(project->values("MSVCPROJ_LIBOPTIONS")); + conf.linker.OutputFile = project->first("DESTDIR"); + break; + case StaticLib: //unhandled - added to remove warnings.. + break; + } + + if(conf.linker.OutputFile.isEmpty()) + conf.linker.OutputFile = ".\\"; + + if(!conf.linker.OutputFile.endsWith("\\")) + conf.linker.OutputFile += '\\'; + + conf.linker.OutputFile += project->first("MSVCPROJ_TARGET"); + + if(project->isActiveConfig("debug")){ + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_DEBUG")); + } else { + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_RELEASE")); + } + + if(project->isActiveConfig("dll")){ + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_QT_DLL")); + } + + if(project->isActiveConfig("console")){ + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_CONSOLE")); + } else { + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_WINDOWS")); + } + +} + +void VcprojGenerator::initResourceTool() +{ + VCConfiguration &conf = vcProject.Configuration; + conf.resource.PreprocessorDefinitions = conf.compiler.PreprocessorDefinitions; + + // We need to add _DEBUG for the debug version of the project, since the normal compiler defines + // do not contain it. (The compiler defines this symbol automatically, which is wy we don't need + // to add it for the compiler) However, the resource tool does not do this. + if(project->isActiveConfig("debug")) + conf.resource.PreprocessorDefinitions += "_DEBUG"; + if(project->isActiveConfig("staticlib")) + conf.resource.ResourceOutputFileName = project->first("DESTDIR") + "/$(InputName).res"; +} + + +void VcprojGenerator::initIDLTool() +{ +} + +void VcprojGenerator::initCustomBuildTool() +{ +} + +void VcprojGenerator::initPreBuildEventTools() +{ +} + +QString VcprojGenerator::fixCommandLine(DotNET version, const QString &input) const +{ + QString result = input; + + if (version >= NET2005) + result = result.replace(QLatin1Char('\n'), QLatin1String("
")); + + return result; +} + +void VcprojGenerator::initPostBuildEventTools() +{ + VCConfiguration &conf = vcProject.Configuration; + if(!project->values("QMAKE_POST_LINK").isEmpty()) { + QString cmdline = fixCommandLine(conf.CompilerVersion, var("QMAKE_POST_LINK")); + conf.postBuild.CommandLine = cmdline; + if (conf.CompilerVersion < NET2005) + cmdline = cmdline.replace("\n", "&&"); + conf.postBuild.Description = cmdline; + } + + QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); + bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && + !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); + if(useSignature) + conf.postBuild.CommandLine.prepend(QLatin1String("signtool sign /F ") + signature + " \"$(TargetPath)\"\n" + + (!conf.postBuild.CommandLine.isEmpty() ? " && " : "")); + + if(!project->values("MSVCPROJ_COPY_DLL").isEmpty()) { + if(!conf.postBuild.CommandLine.isEmpty()) + conf.postBuild.CommandLine += " && "; + conf.postBuild.Description += var("MSVCPROJ_COPY_DLL_DESC"); + conf.postBuild.CommandLine += var("MSVCPROJ_COPY_DLL"); + } +} + +void VcprojGenerator::initDeploymentTool() +{ + VCConfiguration &conf = vcProject.Configuration; + QString targetPath = project->values("deploy.path").join(" "); + if (targetPath.isEmpty()) + targetPath = QString("%CSIDL_PROGRAM_FILES%\\") + project->first("TARGET"); + if (targetPath.endsWith("/") || targetPath.endsWith("\\")) + targetPath = targetPath.mid(0,targetPath.size()-1); + + // Only deploy Qt libs for shared build + if (!project->values("QMAKE_QT_DLL").isEmpty()) { + QStringList& arg = project->values("MSVCPROJ_LIBS"); + for (QStringList::ConstIterator it = arg.constBegin(); it != arg.constEnd(); ++it) { + if (it->contains(project->first("QMAKE_LIBDIR"))) { + QString dllName = *it; + + if (dllName.contains(QLatin1String("QAxContainer")) + || dllName.contains(QLatin1String("qtmain")) + || dllName.contains(QLatin1String("QtUiTools"))) + continue; + dllName.replace(QLatin1String(".lib") , QLatin1String(".dll")); + QFileInfo info(dllName); + conf.deployment.AdditionalFiles += info.fileName() + + "|" + QDir::toNativeSeparators(info.absolutePath()) + + "|" + targetPath + + "|0;"; + } + } + } + + // C-runtime deployment + QString runtime = project->values("QT_CE_C_RUNTIME").join(QLatin1String(" ")); + if (!runtime.isEmpty() && (runtime != QLatin1String("no"))) { + QString runtimeVersion = QLatin1String("msvcr"); + QString mkspec = project->first("QMAKESPEC"); + // If no .qmake.cache has been found, we fallback to the original mkspec + if (mkspec.isEmpty()) + mkspec = project->first("QMAKESPEC_ORIGINAL"); + + if (!mkspec.isEmpty()) { + if (mkspec.endsWith("2008")) + runtimeVersion.append("90"); + else + runtimeVersion.append("80"); + if (project->isActiveConfig("debug")) + runtimeVersion.append("d"); + runtimeVersion.append(".dll"); + + if (runtime == "yes") { + // Auto-find C-runtime + QString vcInstallDir = qgetenv("VCINSTALLDIR"); + if (!vcInstallDir.isEmpty()) { + vcInstallDir += "\\ce\\dll\\"; + vcInstallDir += project->values("CE_ARCH").join(QLatin1String(" ")); + if (!QFileInfo(vcInstallDir + QDir::separator() + runtimeVersion).exists()) + runtime.clear(); + else + runtime = vcInstallDir; + } + } + } + + if (!runtime.isEmpty() && runtime != QLatin1String("yes")) { + conf.deployment.AdditionalFiles += runtimeVersion + + "|" + QDir::toNativeSeparators(runtime) + + "|" + targetPath + + "|0;"; + } + } + + // foreach item in DEPLOYMENT + foreach(QString item, project->values("DEPLOYMENT")) { + // get item.path + QString devicePath = project->first(item + ".path"); + // if the path does not exist, skip it + if (devicePath.isEmpty()) + continue; + // check if item.path is relative (! either /,\ or %) + if (!(devicePath.at(0) == QLatin1Char('/') + || devicePath.at(0) == QLatin1Char('\\') + || devicePath.at(0) == QLatin1Char('%'))) { + // create output path + devicePath = Option::fixPathToLocalOS(QDir::cleanPath(targetPath + QLatin1Char('\\') + devicePath)); + } + // foreach d in item.sources + foreach(QString source, project->values(item + ".sources")) { + QString itemDevicePath = devicePath; + source = Option::fixPathToLocalOS(source); + QString nameFilter; + QFileInfo info(source); + QString searchPath; + if (info.isDir()) { + nameFilter = QLatin1String("*"); + itemDevicePath += "\\" + info.fileName(); + searchPath = info.absoluteFilePath(); + } else { + nameFilter = source.split('\\').last(); + searchPath = info.absolutePath(); + } + + int pathSize = searchPath.size(); + QDirIterator iterator(searchPath, QStringList() << nameFilter + , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks + , QDirIterator::Subdirectories); + // foreach dirIterator-entry in d + while(iterator.hasNext()) { + iterator.next(); + QString absoluteItemPath = Option::fixPathToLocalOS(QFileInfo(iterator.filePath()).absolutePath()); + // Identify if it is just another subdir + int diffSize = absoluteItemPath.size() - pathSize; + // write out rules + conf.deployment.AdditionalFiles += iterator.fileName() + + "|" + absoluteItemPath + + "|" + itemDevicePath + (diffSize ? (absoluteItemPath.right(diffSize)) : QLatin1String("")) + + "|0;"; + } + } + } +} + +void VcprojGenerator::initPreLinkEventTools() +{ + VCConfiguration &conf = vcProject.Configuration; + if(!project->values("QMAKE_PRE_LINK").isEmpty()) { + QString cmdline = fixCommandLine(conf.CompilerVersion, var("QMAKE_PRE_LINK")); + conf.preLink.Description = cmdline; + conf.preLink.CommandLine = cmdline; + } +} + +void VcprojGenerator::initRootFiles() +{ + // Note: Root files do _not_ have any filter name, filter nor GUID! + vcProject.RootFiles.addFiles(project->values("RC_FILE")); + + vcProject.RootFiles.Project = this; + vcProject.RootFiles.Config = &(vcProject.Configuration); + vcProject.RootFiles.CustomBuild = none; +} + +void VcprojGenerator::initSourceFiles() +{ + vcProject.SourceFiles.Name = "Source Files"; + vcProject.SourceFiles.Filter = "cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"; + vcProject.SourceFiles.Guid = _GUIDSourceFiles; + + vcProject.SourceFiles.addFiles(project->values("SOURCES")); + + vcProject.SourceFiles.Project = this; + vcProject.SourceFiles.Config = &(vcProject.Configuration); + vcProject.SourceFiles.CustomBuild = none; +} + +void VcprojGenerator::initHeaderFiles() +{ + vcProject.HeaderFiles.Name = "Header Files"; + vcProject.HeaderFiles.Filter = "h;hpp;hxx;hm;inl;inc;xsd"; + vcProject.HeaderFiles.Guid = _GUIDHeaderFiles; + + vcProject.HeaderFiles.addFiles(project->values("HEADERS")); + if (usePCH) // Generated PCH cpp file + vcProject.HeaderFiles.addFile(precompH); + + vcProject.HeaderFiles.Project = this; + vcProject.HeaderFiles.Config = &(vcProject.Configuration); +// vcProject.HeaderFiles.CustomBuild = mocHdr; +// addMocArguments(vcProject.HeaderFiles); +} + +void VcprojGenerator::initGeneratedFiles() +{ + vcProject.GeneratedFiles.Name = "Generated Files"; + vcProject.GeneratedFiles.Filter = "cpp;c;cxx;moc;h;def;odl;idl;res;"; + vcProject.GeneratedFiles.Guid = _GUIDGeneratedFiles; + + // ### These cannot have CustomBuild (mocSrc)!! + vcProject.GeneratedFiles.addFiles(project->values("GENERATED_SOURCES")); + vcProject.GeneratedFiles.addFiles(project->values("GENERATED_FILES")); + vcProject.GeneratedFiles.addFiles(project->values("IDLSOURCES")); + vcProject.GeneratedFiles.addFiles(project->values("RES_FILE")); + vcProject.GeneratedFiles.addFiles(project->values("QMAKE_IMAGE_COLLECTION")); // compat + if(!extraCompilerOutputs.isEmpty()) + vcProject.GeneratedFiles.addFiles(extraCompilerOutputs.keys()); + + vcProject.GeneratedFiles.Project = this; + vcProject.GeneratedFiles.Config = &(vcProject.Configuration); +// vcProject.GeneratedFiles.CustomBuild = mocSrc; +} + +void VcprojGenerator::initLexYaccFiles() +{ + vcProject.LexYaccFiles.Name = "Lex / Yacc Files"; + vcProject.LexYaccFiles.ParseFiles = _False; + vcProject.LexYaccFiles.Filter = "l;y"; + vcProject.LexYaccFiles.Guid = _GUIDLexYaccFiles; + + vcProject.LexYaccFiles.addFiles(project->values("LEXSOURCES")); + vcProject.LexYaccFiles.addFiles(project->values("YACCSOURCES")); + + vcProject.LexYaccFiles.Project = this; + vcProject.LexYaccFiles.Config = &(vcProject.Configuration); + vcProject.LexYaccFiles.CustomBuild = lexyacc; +} + +void VcprojGenerator::initTranslationFiles() +{ + vcProject.TranslationFiles.Name = "Translation Files"; + vcProject.TranslationFiles.ParseFiles = _False; + vcProject.TranslationFiles.Filter = "ts;xlf"; + vcProject.TranslationFiles.Guid = _GUIDTranslationFiles; + + vcProject.TranslationFiles.addFiles(project->values("TRANSLATIONS")); + + vcProject.TranslationFiles.Project = this; + vcProject.TranslationFiles.Config = &(vcProject.Configuration); + vcProject.TranslationFiles.CustomBuild = none; +} + + +void VcprojGenerator::initFormFiles() +{ + vcProject.FormFiles.Name = "Form Files"; + vcProject.FormFiles.ParseFiles = _False; + vcProject.FormFiles.Filter = "ui"; + vcProject.FormFiles.Guid = _GUIDFormFiles; + + vcProject.FormFiles.addFiles(project->values("FORMS")); + vcProject.FormFiles.addFiles(project->values("FORMS3")); + + vcProject.FormFiles.Project = this; + vcProject.FormFiles.Config = &(vcProject.Configuration); + vcProject.FormFiles.CustomBuild = none; +} + + +void VcprojGenerator::initResourceFiles() +{ + vcProject.ResourceFiles.Name = "Resource Files"; + vcProject.ResourceFiles.ParseFiles = _False; + vcProject.ResourceFiles.Filter = "qrc;*"; //"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;ts;xlf;qrc"; + vcProject.ResourceFiles.Guid = _GUIDResourceFiles; + + // Bad hack, please look away ------------------------------------- + QString rcc_dep_cmd = project->values("rcc.depend_command").join(" "); + if(!rcc_dep_cmd.isEmpty()) { + QStringList qrc_files = project->values("RESOURCES"); + QStringList deps; + if(!qrc_files.isEmpty()) { + for (int i = 0; i < qrc_files.count(); ++i) { + char buff[256]; + QString dep_cmd = replaceExtraCompilerVariables(rcc_dep_cmd, qrc_files.at(i),""); + + dep_cmd = Option::fixPathToLocalOS(dep_cmd, true, false); + if(canExecute(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()) + deps += fileFixify(indeps.replace('\n', ' ').simplified().split(' ')); + } + } + } + vcProject.ResourceFiles.addFiles(deps); + } + } + // You may look again -------------------------------------------- + + vcProject.ResourceFiles.addFiles(project->values("RESOURCES")); + vcProject.ResourceFiles.addFiles(project->values("IMAGES")); + + vcProject.ResourceFiles.Project = this; + vcProject.ResourceFiles.Config = &(vcProject.Configuration); + vcProject.ResourceFiles.CustomBuild = none; +} + +void VcprojGenerator::initExtraCompilerOutputs() +{ + QStringList otherFilters; + otherFilters << "FORMS" + << "FORMS3" + << "GENERATED_FILES" + << "GENERATED_SOURCES" + << "HEADERS" + << "IDLSOURCES" + << "IMAGES" + << "LEXSOURCES" + << "QMAKE_IMAGE_COLLECTION" + << "RC_FILE" + << "RESOURCES" + << "RES_FILE" + << "SOURCES" + << "TRANSLATIONS" + << "YACCSOURCES"; + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString extracompilerName = project->first((*it) + ".name"); + if (extracompilerName.isEmpty()) + extracompilerName = (*it); + + // Create an extra compiler filter and add the files + VCFilter extraCompile; + extraCompile.Name = extracompilerName; + extraCompile.ParseFiles = _False; + extraCompile.Filter = ""; + extraCompile.Guid = QString(_GUIDExtraCompilerFiles) + "-" + (*it); + + + // If the extra compiler has a variable_out set the output file + // is added to an other file list, and does not need its own.. + bool addOnInput = hasBuiltinCompiler(project->first((*it) + ".output")); + QString tmp_other_out = project->first((*it) + ".variable_out"); + if (!tmp_other_out.isEmpty() && !addOnInput) + continue; + + if (!addOnInput) { + QString tmp_out = project->first((*it) + ".output"); + if (project->values((*it) + ".CONFIG").indexOf("combine") != -1) { + // Combined output, only one file result + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(tmp_out, QString(), QString()), false)); + } else { + // One output file per input + QStringList tmp_in = project->values(project->first((*it) + ".input")); + for (int i = 0; i < tmp_in.count(); ++i) { + const QString &filename = tmp_in.at(i); + if (extraCompilerSources.contains(filename)) + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(filename, tmp_out, QString()), false)); + } + } + } else { + // In this case we the outputs have a built-in compiler, so we cannot add the custom + // build steps there. So, we turn it around and add it to the input files instead, + // provided that the input file variable is not handled already (those in otherFilters + // are handled, so we avoid them). + QStringList inputVars = project->values((*it) + ".input"); + foreach(QString inputVar, inputVars) { + if (!otherFilters.contains(inputVar)) { + QStringList tmp_in = project->values(inputVar); + for (int i = 0; i < tmp_in.count(); ++i) { + const QString &filename = tmp_in.at(i); + if (extraCompilerSources.contains(filename)) + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(filename, QString(), QString()), false)); + } + } + } + } + extraCompile.Project = this; + extraCompile.Config = &(vcProject.Configuration); + extraCompile.CustomBuild = none; + + vcProject.ExtraCompilersFiles.append(extraCompile); + } +} + +/* \internal + Sets up all needed variables from the environment and all the different caches and .conf files +*/ + +void VcprojGenerator::initOld() +{ + if(init_flag) + return; + + init_flag = true; + QStringList::Iterator it; + + // Decode version, and add it to $$MSVCPROJ_VERSION -------------- + if(!project->values("VERSION").isEmpty()) { + QString version = project->values("VERSION")[0]; + int firstDot = version.indexOf("."); + QString major = version.left(firstDot); + QString minor = version.right(version.length() - firstDot - 1); + minor.replace(QRegExp("\\."), ""); + project->values("MSVCPROJ_VERSION").append("/VERSION:" + major + "." + minor); + project->values("QMAKE_LFLAGS").append("/VERSION:" + major + "." + minor); + } + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + + // Get filename w/o extension ----------------------------------- + QString msvcproj_project = ""; + QString targetfilename = ""; + if(!project->isEmpty("TARGET")) { + project->values("TARGET") = unescapeFilePaths(project->values("TARGET")); + targetfilename = msvcproj_project = project->first("TARGET"); + } + + // Init base class too ------------------------------------------- + MakefileGenerator::init(); + + if(msvcproj_project.isEmpty()) + msvcproj_project = Option::output.fileName(); + + msvcproj_project = msvcproj_project.right(msvcproj_project.length() - msvcproj_project.lastIndexOf("\\") - 1); + msvcproj_project = msvcproj_project.left(msvcproj_project.lastIndexOf(".")); + msvcproj_project.replace(QRegExp("-"), ""); + + project->values("MSVCPROJ_PROJECT").append(msvcproj_project); + QStringList &proj = project->values("MSVCPROJ_PROJECT"); + + for(it = proj.begin(); it != proj.end(); ++it) + (*it).replace(QRegExp("\\.[a-zA-Z0-9_]*$"), ""); + + // SUBSYSTEM ----------------------------------------------------- + if(!project->values("QMAKE_APP_FLAG").isEmpty()) { + project->values("MSVCPROJ_TEMPLATE").append("win32app" + project->first("VCPROJ_EXTENSION")); + if(project->isActiveConfig("console")) { + project->values("MSVCPROJ_CONSOLE").append("CONSOLE"); + project->values("MSVCPROJ_WINCONDEF").append("_CONSOLE"); + project->values("MSVCPROJ_VCPROJTYPE").append("0x0103"); + project->values("MSVCPROJ_SUBSYSTEM").append("CONSOLE"); + } else { + project->values("MSVCPROJ_CONSOLE").clear(); + project->values("MSVCPROJ_WINCONDEF").append("_WINDOWS"); + project->values("MSVCPROJ_VCPROJTYPE").append("0x0101"); + project->values("MSVCPROJ_SUBSYSTEM").append("WINDOWS"); + } + } + + // $$QMAKE.. -> $$MSVCPROJ.. ------------------------------------- + project->values("MSVCPROJ_LIBS") += project->values("QMAKE_LIBS"); + project->values("MSVCPROJ_LFLAGS") += project->values("QMAKE_LFLAGS"); + if(!project->values("QMAKE_LIBDIR").isEmpty()) { + QStringList strl = project->values("QMAKE_LIBDIR"); + QStringList::iterator stri; + for(stri = strl.begin(); stri != strl.end(); ++stri) { + if(!(*stri).startsWith("/LIBPATH:")) + (*stri).prepend("/LIBPATH:"); + } + project->values("MSVCPROJ_LFLAGS") += strl; + } + project->values("MSVCPROJ_CXXFLAGS") += project->values("QMAKE_CXXFLAGS"); + QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) { + QString inc = (*incit); + if (!inc.startsWith('"') && !inc.endsWith('"')) + inc = QString("\"%1\"").arg(inc); // Quote all paths if not quoted already + project->values("MSVCPROJ_INCPATH").append("-I" + inc); + } + project->values("MSVCPROJ_INCPATH").append("-I" + specdir()); + + QString dest; + project->values("MSVCPROJ_TARGET") = QStringList(project->first("TARGET")); + Option::fixPathToTargetOS(project->first("TARGET")); + dest = project->first("TARGET") + project->first("TARGET_EXT"); + project->values("MSVCPROJ_TARGET") = QStringList(dest); + + // DLL COPY ------------------------------------------------------ + if(project->isActiveConfig("dll") && !project->values("DLLDESTDIR").isEmpty()) { + QStringList dlldirs = project->values("DLLDESTDIR"); + QString copydll(""); + QStringList::Iterator dlldir; + for(dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { + if(!copydll.isEmpty()) + copydll += " && "; + copydll += "copy \"$(TargetPath)\" \"" + *dlldir + "\""; + } + + QString deststr("Copy " + dest + " to "); + for(dlldir = dlldirs.begin(); dlldir != dlldirs.end();) { + deststr += *dlldir; + ++dlldir; + if(dlldir != dlldirs.end()) + deststr += ", "; + } + + project->values("MSVCPROJ_COPY_DLL").append(copydll); + project->values("MSVCPROJ_COPY_DLL_DESC").append(deststr); + } + + if (!project->values("DEF_FILE").isEmpty()) + project->values("MSVCPROJ_LFLAGS").append("/DEF:"+project->first("DEF_FILE")); + + project->values("QMAKE_INTERNAL_PRL_LIBS") << "MSVCPROJ_LIBS"; + + // Verbose output if "-d -d"... + outputVariables(); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ + +QString VcprojGenerator::replaceExtraCompilerVariables(const QString &var, const QStringList &in, const QStringList &out) +{ + QString ret = MakefileGenerator::replaceExtraCompilerVariables(var, in, out); + + QStringList &defines = project->values("VCPROJ_MAKEFILE_DEFINES"); + if(defines.isEmpty()) + defines.append(varGlue("PRL_EXPORT_DEFINES"," -D"," -D","") + + varGlue("DEFINES"," -D"," -D","")); + ret.replace("$(DEFINES)", defines.first()); + + QStringList &incpath = project->values("VCPROJ_MAKEFILE_INCPATH"); + if(incpath.isEmpty() && !this->var("MSVCPROJ_INCPATH").isEmpty()) + incpath.append(this->var("MSVCPROJ_INCPATH")); + ret.replace("$(INCPATH)", incpath.join(" ")); + + return ret; +} + + + +bool VcprojGenerator::openOutput(QFile &file, const QString &/*build*/) const +{ + QString outdir; + if(!file.fileName().isEmpty()) { + QFileInfo fi(fileInfo(file.fileName())); + if(fi.isDir()) + outdir = file.fileName() + QDir::separator(); + } + if(!outdir.isEmpty() || file.fileName().isEmpty()) { + QString ext = project->first("VCPROJ_EXTENSION"); + if(project->first("TEMPLATE") == "vcsubdirs") + ext = project->first("VCSOLUTION_EXTENSION"); + QString outputName = unescapeFilePath(project->first("TARGET")); + if (!project->first("MAKEFILE").isEmpty()) + outputName = project->first("MAKEFILE"); + file.setFileName(outdir + outputName + ext); + } + return Win32MakefileGenerator::openOutput(file, QString()); +} + +QString VcprojGenerator::fixFilename(QString ofile) const +{ + ofile = Option::fixPathToLocalOS(ofile); + int slashfind = ofile.lastIndexOf(Option::dir_sep); + if(slashfind == -1) { + ofile = ofile.replace('-', '_'); + } else { + int hypenfind = ofile.indexOf('-', slashfind); + while (hypenfind != -1 && slashfind < hypenfind) { + ofile = ofile.replace(hypenfind, 1, '_'); + hypenfind = ofile.indexOf('-', hypenfind + 1); + } + } + return ofile; +} + +QString VcprojGenerator::findTemplate(QString file) +{ + QString ret; + if(!exists((ret = file)) && + !exists((ret = QString(Option::mkfile::qmakespec + "/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc.net/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2002/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2003/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2005/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2008/" + file))) && + !exists((ret = (QString(qgetenv("HOME")) + "/.tmake/" + file)))) + return ""; + debug_msg(1, "Generator: MSVC.NET: Found template \'%s\'", ret.toLatin1().constData()); + return ret; +} + + +void VcprojGenerator::processPrlVariable(const QString &var, const QStringList &l) +{ + if(var == "QMAKE_PRL_DEFINES") { + QStringList &out = project->values("MSVCPROJ_DEFINES"); + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(out.indexOf((*it)) == -1) + out.append((" /D " + *it)); + } + } else { + MakefileGenerator::processPrlVariable(var, l); + } +} + +void VcprojGenerator::outputVariables() +{ +#if 0 + qDebug("Generator: MSVC.NET: List of current variables:"); + for(QMap<QString, QStringList>::ConstIterator it = project->variables().begin(); it != project->variables().end(); ++it) + qDebug("Generator: MSVC.NET: %s => %s", it.key().toLatin1().constData(), it.data().join(" | ").toLatin1().constData()); +#endif +} + +QT_END_NAMESPACE + diff --git a/qmake/generators/win32/msvc_vcproj.h b/qmake/generators/win32/msvc_vcproj.h new file mode 100644 index 0000000..9e7d725 --- /dev/null +++ b/qmake/generators/win32/msvc_vcproj.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_VCPROJ_H +#define MSVC_VCPROJ_H + +#include "winmakefile.h" +#include "msvc_objectmodel.h" + +QT_BEGIN_NAMESPACE + +enum Target { + Application, + SharedLib, + StaticLib +}; + +struct QUuid; +class VcprojGenerator : public Win32MakefileGenerator +{ + bool init_flag; + bool writeVcprojParts(QTextStream &); + + bool writeMakefile(QTextStream &); + bool writeProjectMakefile(); + void writeSubDirs(QTextStream &t); + + QString findTemplate(QString file); + void init(); + +public: + VcprojGenerator(); + ~VcprojGenerator(); + + QString defaultMakefile() const; + QString precompH, precompHFilename, precompCPP, + precompObj, precompPch; + bool autogenPrecompCPP; + static bool hasBuiltinCompiler(const QString &file); + + QMap<QString, QStringList> extraCompilerSources; + QMap<QString, QStringList> extraCompilerOutputs; + bool usePCH; + +protected: + virtual bool doDepends() const { return false; } //never necesary + virtual void processSources() { filterIncludedFiles("SOURCES"); filterIncludedFiles("GENERATED_SOURCES"); } + virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &); + inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out) + { return MakefileGenerator::replaceExtraCompilerVariables(val, in, out); } + virtual bool supportsMetaBuild() { return true; } + virtual bool supportsMergedBuilds() { return true; } + virtual bool mergeBuildProject(MakefileGenerator *other); + + virtual bool openOutput(QFile &file, const QString &build) const; + virtual void processPrlVariable(const QString &, const QStringList &); + virtual bool findLibraries(); + virtual void outputVariables(); + QString fixFilename(QString ofile) const; + + void initOld(); + virtual void initProject(); + void initConfiguration(); + void initCompilerTool(); + void initLinkerTool(); + void initLibrarianTool(); + void initResourceTool(); + void initIDLTool(); + void initCustomBuildTool(); + void initPreBuildEventTools(); + void initPostBuildEventTools(); + void initDeploymentTool(); + void initPreLinkEventTools(); + void initRootFiles(); + void initSourceFiles(); + void initHeaderFiles(); + void initGeneratedFiles(); + void initTranslationFiles(); + void initFormFiles(); + void initResourceFiles(); + void initLexYaccFiles(); + void initExtraCompilerOutputs(); + + Target projectTarget; + + // Used for single project + VCProjectSingleConfig vcProject; + + // Holds all configurations for glue (merged) project + QList<VcprojGenerator*> mergedProjects; + +private: + QString fixCommandLine(DotNET version, const QString &input) const; + QUuid getProjectUUID(const QString &filename=QString()); + QUuid increaseUUID(const QUuid &id); + friend class VCFilter; +}; + +inline VcprojGenerator::~VcprojGenerator() +{ } + +inline QString VcprojGenerator::defaultMakefile() const +{ + return project->first("TARGET") + project->first("VCPROJ_EXTENSION"); +} + +inline bool VcprojGenerator::findLibraries() +{ + return Win32MakefileGenerator::findLibraries("MSVCPROJ_LIBS"); +} + +QT_END_NAMESPACE + +#endif // MSVC_VCPROJ_H diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp new file mode 100644 index 0000000..60a27be --- /dev/null +++ b/qmake/generators/win32/winmakefile.cpp @@ -0,0 +1,821 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "winmakefile.h" +#include "option.h" +#include "project.h" +#include "meta.h" +#include <qtextstream.h> +#include <qstring.h> +#include <qhash.h> +#include <qregexp.h> +#include <qstringlist.h> +#include <qdir.h> +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +Win32MakefileGenerator::Win32MakefileGenerator() : MakefileGenerator() +{ +} + +int +Win32MakefileGenerator::findHighestVersion(const QString &d, const QString &stem, const QString &ext) +{ + QString bd = Option::fixPathToLocalOS(d, true); + if(!exists(bd)) + return -1; + + QString dllStem = stem + QTDLL_POSTFIX; + QMakeMetaInfo libinfo; + bool libInfoRead = libinfo.readLib(bd + Option::dir_sep + dllStem); + + // If the library, for which we're trying to find the highest version + // number, is a static library + if (libInfoRead && libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib")) + return -1; + + if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty()) + return project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first().toInt(); + + int biggest=-1; + if(!project->isActiveConfig("no_versionlink")) { + QDir dir(bd); + QStringList entries = dir.entryList(); + QRegExp regx(QString("((lib)?%1([0-9]*)).(%2|prl)$").arg(dllStem).arg(ext), Qt::CaseInsensitive); + for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) { + if(regx.exactMatch((*it))) { + if (!regx.cap(3).isEmpty()) { + bool ok = true; + int num = regx.cap(3).toInt(&ok); + biggest = qMax(biggest, (!ok ? -1 : num)); + } + } + } + } + if(libInfoRead + && !libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib") + && !libinfo.isEmpty("QMAKE_PRL_VERSION")) + biggest = qMax(biggest, libinfo.first("QMAKE_PRL_VERSION").replace(".", "").toInt()); + return biggest; +} + +bool +Win32MakefileGenerator::findLibraries(const QString &where) +{ + QStringList &l = project->values(where); + QList<QMakeLocalFileName> dirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); + libpathit != libpaths.end(); ++libpathit) + dirs.append(QMakeLocalFileName((*libpathit))); + } + for(QStringList::Iterator it = l.begin(); it != l.end();) { + QChar quote; + bool modified_opt = false, remove = false; + QString opt = (*it).trimmed(); + if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) { + quote = opt[0]; + opt = opt.mid(1, opt.length()-2); + } + if(opt.startsWith("/LIBPATH:")) { + dirs.append(QMakeLocalFileName(opt.mid(9))); + } else if(opt.startsWith("-L") || opt.startsWith("/L")) { + QString libpath = opt.mid(2); + QMakeLocalFileName l(libpath); + if(!dirs.contains(l)) { + dirs.append(l); + modified_opt = true; + if (!quote.isNull()) { + libpath = quote + libpath + quote; + quote = QChar(); + } + (*it) = "/LIBPATH:" + libpath; + } else { + remove = true; + } + } else if(opt.startsWith("-l") || opt.startsWith("/l")) { + QString lib = opt.right(opt.length() - 2), out; + if(!lib.isEmpty()) { + QString suffix; + if(!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + lib.toUpper() + "_SUFFIX"); + for(QList<QMakeLocalFileName>::Iterator it = dirs.begin(); + it != dirs.end(); ++it) { + QString extension; + int ver = findHighestVersion((*it).local(), lib); + if(ver > 0) + extension += QString::number(ver); + extension += suffix; + extension += ".lib"; + if(QMakeMetaInfo::libExists((*it).local() + Option::dir_sep + lib) || + exists((*it).local() + Option::dir_sep + lib + extension)) { + out = (*it).real() + Option::dir_sep + lib + extension; + break; + } + } + } + if(out.isEmpty()) + out = lib + ".lib"; + modified_opt = true; + (*it) = out; + } else if(!exists(Option::fixPathToLocalOS(opt))) { + QList<QMakeLocalFileName> lib_dirs; + QString file = opt; + int slsh = file.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + lib_dirs.append(QMakeLocalFileName(file.left(slsh+1))); + file = file.right(file.length() - slsh - 1); + } else { + lib_dirs = dirs; + } + if(file.endsWith(".lib")) { + file = file.left(file.length() - 4); + if(!file.at(file.length()-1).isNumber()) { + QString suffix; + if(!project->isEmpty("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX"); + for(QList<QMakeLocalFileName>::Iterator dep_it = lib_dirs.begin(); dep_it != lib_dirs.end(); ++dep_it) { + QString lib_tmpl(file + "%1" + suffix + ".lib"); + int ver = findHighestVersion((*dep_it).local(), file); + if(ver != -1) { + if(ver) + lib_tmpl = lib_tmpl.arg(ver); + else + lib_tmpl = lib_tmpl.arg(""); + if(slsh != -1) { + QString dir = (*dep_it).real(); + if(!dir.endsWith(Option::dir_sep)) + dir += Option::dir_sep; + lib_tmpl.prepend(dir); + } + modified_opt = true; + (*it) = lib_tmpl; + break; + } + } + } + } + } + if(remove) { + it = l.erase(it); + } else { + if(!quote.isNull() && modified_opt) + (*it) = quote + (*it) + quote; + ++it; + } + } + return true; +} + +void +Win32MakefileGenerator::processPrlFiles() +{ + QHash<QString, bool> processed; + QList<QMakeLocalFileName> libdirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); libpathit != libpaths.end(); ++libpathit) + libdirs.append(QMakeLocalFileName((*libpathit))); + } + 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 opt = (*it).trimmed(); + if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) + opt = opt.mid(1, opt.length()-2); + if(opt.startsWith("/")) { + if(opt.startsWith("/LIBPATH:")) { + QMakeLocalFileName l(opt.mid(9)); + if(!libdirs.contains(l)) + libdirs.append(l); + } + } else if(!processed.contains(opt)) { + if(processPrlFile(opt)) { + processed.insert(opt, true); + ret = true; + } else if(QDir::isRelativePath(opt) || opt.startsWith("-l")) { + QString tmp; + if (opt.startsWith("-l")) + tmp = opt.mid(2); + else + tmp = opt; + for(QList<QMakeLocalFileName>::Iterator it = libdirs.begin(); it != libdirs.end(); ++it) { + QString prl = (*it).local() + Option::dir_sep + tmp; + // the original is used as the key + QString orgprl = prl; + if(processed.contains(prl)) { + break; + } else if(processPrlFile(prl)) { + processed.insert(orgprl, true); + ret = true; + break; + } + } + } + } + if(!opt.isEmpty()) + l_out.append(opt); + } + if(ret) + l = l_out; + else + break; + } +} + + +void Win32MakefileGenerator::processVars() +{ + //If the TARGET looks like a path split it into DESTDIR and the resulting TARGET + if(!project->isEmpty("TARGET")) { + QString targ = 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"); + if (!project->values("QMAKE_INCDIR").isEmpty()) + project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR"); + + if (!project->values("VERSION").isEmpty()) { + QStringList l = project->first("VERSION").split('.'); + if (l.size() > 0) + project->values("VER_MAJ").append(l[0]); + if (l.size() > 1) + project->values("VER_MIN").append(l[1]); + } + + // TARGET_VERSION_EXT will be used to add a version number onto the target name + if (project->values("TARGET_VERSION_EXT").isEmpty() + && !project->values("VER_MAJ").isEmpty()) + project->values("TARGET_VERSION_EXT").append(project->values("VER_MAJ").first()); + + if(project->isEmpty("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + + fixTargetExt(); + processRcFileVar(); + processFileTagsVar(); + + QStringList &incDir = project->values("INCLUDEPATH"); + for(QStringList::Iterator incDir_it = incDir.begin(); incDir_it != incDir.end(); ++incDir_it) { + if(!(*incDir_it).isEmpty()) + (*incDir_it) = Option::fixPathToTargetOS((*incDir_it), false, false); + } + QStringList &libDir = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) { + if(!(*libDir_it).isEmpty()) + (*libDir_it) = Option::fixPathToTargetOS((*libDir_it), false, false); + } +} + +void Win32MakefileGenerator::fixTargetExt() +{ + if (!project->values("QMAKE_APP_FLAG").isEmpty()) + project->values("TARGET_EXT").append(".exe"); + else if (project->isActiveConfig("shared")) + project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + ".dll"); + else + project->values("TARGET_EXT").append(".lib"); +} + +void Win32MakefileGenerator::processRcFileVar() +{ + if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING) + return; + + if (((!project->values("VERSION").isEmpty()) + && project->values("RC_FILE").isEmpty() + && project->values("RES_FILE").isEmpty() + && !project->isActiveConfig("no_generated_target_info") + && (project->isActiveConfig("shared") || !project->values("QMAKE_APP_FLAG").isEmpty())) + || !project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty()){ + + QByteArray rcString; + QTextStream ts(&rcString, QFile::WriteOnly); + + QStringList vers = project->values("VERSION").first().split("."); + for (int i = vers.size(); i < 4; i++) + vers += "0"; + QString versionString = vers.join("."); + + QString companyName; + if (!project->values("QMAKE_TARGET_COMPANY").isEmpty()) + companyName = project->values("QMAKE_TARGET_COMPANY").join(" "); + + QString description; + if (!project->values("QMAKE_TARGET_DESCRIPTION").isEmpty()) + description = project->values("QMAKE_TARGET_DESCRIPTION").join(" "); + + QString copyright; + if (!project->values("QMAKE_TARGET_COPYRIGHT").isEmpty()) + copyright = project->values("QMAKE_TARGET_COPYRIGHT").join(" "); + + QString productName; + if (!project->values("QMAKE_TARGET_PRODUCT").isEmpty()) + productName = project->values("QMAKE_TARGET_PRODUCT").join(" "); + else + productName = project->values("TARGET").first(); + + QString originalName = project->values("TARGET").first() + project->values("TARGET_EXT").first(); + + ts << "# if defined(UNDER_CE)" << endl; + ts << "# include <winbase.h>" << endl; + ts << "# else" << endl; + ts << "# include <winver.h>" << endl; + ts << "# endif" << endl; + ts << endl; + ts << "VS_VERSION_INFO VERSIONINFO" << endl; + ts << "\tFILEVERSION " << QString(versionString).replace(".", ",") << endl; + ts << "\tPRODUCTVERSION " << QString(versionString).replace(".", ",") << endl; + ts << "\tFILEFLAGSMASK 0x3fL" << endl; + ts << "#ifdef _DEBUG" << endl; + ts << "\tFILEFLAGS VS_FF_DEBUG" << endl; + ts << "#else" << endl; + ts << "\tFILEFLAGS 0x0L" << endl; + ts << "#endif" << endl; + ts << "\tFILEOS VOS__WINDOWS32" << endl; + if (project->isActiveConfig("shared")) + ts << "\tFILETYPE VFT_DLL" << endl; + else + ts << "\tFILETYPE VFT_APP" << endl; + ts << "\tFILESUBTYPE 0x0L" << endl; + ts << "\tBEGIN" << endl; + ts << "\t\tBLOCK \"StringFileInfo\"" << endl; + ts << "\t\tBEGIN" << endl; + ts << "\t\t\tBLOCK \"040904B0\"" << endl; + ts << "\t\t\tBEGIN" << endl; + ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"FileDescription\", \"" << description << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"" << endl; + ts << "\t\t\tEND" << endl; + ts << "\t\tEND" << endl; + ts << "\tEND" << endl; + ts << "/* End of Version info */" << endl; + ts << endl; + + ts.flush(); + + + QString rcFilename = project->values("OUT_PWD").first() + + "/" + + project->values("TARGET").first() + + "_resource" + + ".rc"; + QFile rcFile(QDir::cleanPath(rcFilename)); + + bool writeRcFile = true; + if (rcFile.exists() && rcFile.open(QFile::ReadOnly)) { + writeRcFile = rcFile.readAll() != rcString; + rcFile.close(); + } + if (writeRcFile && rcFile.open(QFile::WriteOnly)) { + rcFile.write(rcString); + rcFile.close(); + } + if (project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty()) + project->values("RC_FILE").insert(0, rcFile.fileName()); + } + if (!project->values("RC_FILE").isEmpty()) { + if (!project->values("RES_FILE").isEmpty()) { + fprintf(stderr, "Both rc and res file specified.\n"); + fprintf(stderr, "Please specify one of them, not both."); + exit(1); + } + QString resFile = project->values("RC_FILE").first(); + + // if this is a shadow build then use the absolute path of the rc file + if (Option::output_dir != qmake_getpwd()) { + QFileInfo fi(resFile); + project->values("RC_FILE").first() = fi.absoluteFilePath(); + } + + resFile.replace(".rc", Option::res_ext); + project->values("RES_FILE").prepend(fileInfo(resFile).fileName()); + if (!project->values("OBJECTS_DIR").isEmpty()) + if(project->isActiveConfig("staticlib")) + project->values("RES_FILE").first().prepend(fileInfo(project->values("DESTDIR").first()).absoluteFilePath() + Option::dir_sep); + else + project->values("RES_FILE").first().prepend(project->values("OBJECTS_DIR").first() + Option::dir_sep); + project->values("RES_FILE").first() = Option::fixPathToTargetOS(project->values("RES_FILE").first(), false, false); + project->values("POST_TARGETDEPS") += project->values("RES_FILE"); + project->values("CLEAN_FILES") += project->values("RES_FILE"); + } +} + +void Win32MakefileGenerator::processFileTagsVar() +{ + QStringList tags; + tags << "SOURCES" << "GENERATED_SOURCES" << "DEF_FILE" << "RC_FILE" + << "TARGET" << "QMAKE_LIBS" << "DESTDIR" << "DLLDESTDIR" << "INCLUDEPATH"; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) + tags += project->values((*it)+".input"); + } + + //clean path + QStringList &filetags = project->values("QMAKE_FILETAGS"); + for(int i = 0; i < tags.size(); ++i) + filetags += Option::fixPathToTargetOS(tags.at(i), false); +} + +void Win32MakefileGenerator::writeCleanParts(QTextStream &t) +{ + t << "clean: compiler_clean " << var("CLEAN_DEPS"); + { + const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", 0 }; + for(int i = 0; clean_targets[i]; ++i) { + const QStringList &list = project->values(clean_targets[i]); + const QString del_statement("-$(DEL_FILE)"); + if(project->isActiveConfig("no_delete_multiple_files")) { + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + t << "\n\t" << del_statement << " " << escapeFilePath((*it)); + } else { + QString files, file; + const int commandlineLimit = 2047; // NT limit, expanded + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + file = " " + escapeFilePath((*it)); + if(del_statement.length() + files.length() + + qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) { + t << "\n\t" << del_statement << files; + files.clear(); + } + files += file; + } + if(!files.isEmpty()) + t << "\n\t" << del_statement << files; + } + } + } + t << endl << endl; + + t << "distclean: clean"; + { + const char *clean_targets[] = { "QMAKE_DISTCLEAN", 0 }; + for(int i = 0; clean_targets[i]; ++i) { + const QStringList &list = project->values(clean_targets[i]); + const QString del_statement("-$(DEL_FILE)"); + if(project->isActiveConfig("no_delete_multiple_files")) { + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + t << "\n\t" << del_statement << " " << escapeFilePath((*it)); + } else { + QString files, file; + const int commandlineLimit = 2047; // NT limit, expanded + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + file = " " + escapeFilePath((*it)); + if(del_statement.length() + files.length() + + qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) { + t << "\n\t" << del_statement << files; + files.clear(); + } + files += file; + } + if(!files.isEmpty()) + t << "\n\t" << del_statement << files; + } + } + } + t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" << endl; + { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + } + t << endl; +} + +void Win32MakefileGenerator::writeIncPart(QTextStream &t) +{ + t << "INCPATH = "; + + const QStringList &incs = project->values("INCLUDEPATH"); + for(int i = 0; i < incs.size(); ++i) { + QString inc = incs.at(i); + inc.replace(QRegExp("\\\\$"), ""); + inc.replace(QRegExp("\""), ""); + if(!inc.isEmpty()) + t << "-I" << "\"" << inc << "\" "; + } + t << "-I\"" << specdir() << "\"" + << endl; +} + +void Win32MakefileGenerator::writeStandardParts(QTextStream &t) +{ + 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; + + writeIncPart(t); + writeLibsPart(t); + + t << "QMAKE = " << (project->isEmpty("QMAKE_QMAKE") ? QString("qmake") : + Option::fixPathToTargetOS(var("QMAKE_QMAKE"), false)) << endl; + t << "IDC = " << (project->isEmpty("QMAKE_IDC") ? QString("idc") : + Option::fixPathToTargetOS(var("QMAKE_IDC"), false)) << endl; + t << "IDL = " << (project->isEmpty("QMAKE_IDL") ? QString("midl") : + Option::fixPathToTargetOS(var("QMAKE_IDL"), false)) << endl; + t << "ZIP = " << var("QMAKE_ZIP") << endl; + t << "DEF_FILE = " << varList("DEF_FILE") << endl; + t << "RES_FILE = " << varList("RES_FILE") << endl; // Not on mingw, can't see why not though... + t << "COPY = " << var("QMAKE_COPY") << endl; + t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; + t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << 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 << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; + t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; + t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; + t << endl; + + t << "####### Output directory" << endl << endl; + if(!project->values("OBJECTS_DIR").isEmpty()) + t << "OBJECTS_DIR = " << var("OBJECTS_DIR").replace(QRegExp("\\\\$"),"") << endl; + else + t << "OBJECTS_DIR = . " << endl; + t << endl; + + t << "####### Files" << endl << endl; + t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES"))) + << " " << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << endl; + + // do this here so we can set DEST_TARGET to be the complete path to the final target if it is needed. + QString orgDestDir = var("DESTDIR"); + QString destDir = Option::fixPathToTargetOS(orgDestDir, false); + if (orgDestDir.endsWith('/') || orgDestDir.endsWith(Option::dir_sep)) + destDir += Option::dir_sep; + QString target = QString(project->first("TARGET")+project->first("TARGET_EXT")); + target.remove("\""); + project->values("DEST_TARGET").prepend(destDir + target); + + writeObjectsPart(t); + + writeExtraCompilerVariables(t); + writeExtraVariables(t); + + t << "DIST = " << varList("DISTFILES") << endl; + t << "QMAKE_TARGET = " << var("QMAKE_ORIG_TARGET") << endl; + // The comment is important to maintain variable compatability with Unix + // Makefiles, while not interpreting a trailing-slash as a linebreak + t << "DESTDIR = " << escapeFilePath(destDir) << " #avoid trailing-slash linebreak" << endl; + t << "TARGET = " << escapeFilePath(target) << endl; + t << "DESTDIR_TARGET = " << escapeFilePath(var("DEST_TARGET")) << endl; + t << endl; + + t << "####### Implicit rules" << endl << endl; + writeImplicitRulesPart(t); + + t << "####### Build rules" << endl << endl; + writeBuildRulesPart(t); + + if(project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) { + QStringList dlldirs = project->values("DLLDESTDIR"); + for (QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { + t << "\n\t" << "-$(COPY_FILE) \"$(DESTDIR_TARGET)\" " << Option::fixPathToTargetOS(*dlldir, false); + } + } + t << endl << endl; + + writeRcFilePart(t); + + writeMakeQmake(t); + + QStringList dist_files = fileFixify(Option::mkfile::project_files); + if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES")) + dist_files += project->values("QMAKE_INTERNAL_INCLUDED_FILES"); + if(!project->isEmpty("TRANSLATIONS")) + dist_files << var("TRANSLATIONS"); + if(!project->isEmpty("FORMS")) { + QStringList &forms = project->values("FORMS"); + for(QStringList::Iterator formit = forms.begin(); formit != forms.end(); ++formit) { + QString ui_h = fileFixify((*formit) + Option::h_ext.first()); + if(exists(ui_h)) + dist_files << ui_h; + } + } + t << "dist:" << "\n\t" + << "$(ZIP) " << var("QMAKE_ORIG_TARGET") << ".zip " << "$(SOURCES) $(DIST) " + << dist_files.join(" ") << " " << var("TRANSLATIONS") << " "; + 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 &inputs = project->values((*it)+".input"); + for(QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) { + t << (*input) << " "; + } + } + } + t << endl << endl; + + writeCleanParts(t); + writeExtraTargets(t); + writeExtraCompilerTargets(t); + t << endl << endl; +} + +void Win32MakefileGenerator::writeLibsPart(QTextStream &t) +{ + if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + t << "LIBAPP = " << var("QMAKE_LIB") << endl; + t << "LIBFLAGS = " << var("QMAKE_LIBFLAGS") << endl; + } else { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = "; + if(!project->values("QMAKE_LIBDIR").isEmpty()) + writeLibDirPart(t); + t << var("QMAKE_LFLAGS") << endl; + t << "LIBS = " << var("QMAKE_LIBS") << endl; + } +} + +void Win32MakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + for (int i = 0; i < libDirs.size(); ++i) + libDirs[i].remove("\""); + t << valGlue(libDirs,"-L\"","\" -L\"","\"") << " "; +} + +void Win32MakefileGenerator::writeObjectsPart(QTextStream &t) +{ + t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << endl; +} + +void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &t) +{ + t << ".SUFFIXES:"; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) + t << " " << (*cppit); + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << " " << (*cit); + 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; +} + +void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &) +{ +} + +void Win32MakefileGenerator::writeRcFilePart(QTextStream &t) +{ + if(!project->values("RC_FILE").isEmpty()) { + const QString res_file = project->first("RES_FILE"), + rc_file = fileFixify(project->first("RC_FILE")); + // The resource tool needs to have the same defines passed in as the compiler, since you may + // use these defines in the .rc file itself. Also, we need to add the _DEBUG define manually + // since the compiler defines this symbol by itself, and we use it in the automatically + // created rc file when VERSION is define the .pro file. + t << res_file << ": " << rc_file << "\n\t" + << var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "") << " $(DEFINES) -fo " << res_file << " " << rc_file; + t << endl << endl; + } +} + +QString Win32MakefileGenerator::getLibTarget() +{ + return QString(project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".lib"); +} + +QString Win32MakefileGenerator::defaultInstall(const QString &t) +{ + if((t != "target" && t != "dlltarget") || + (t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) || + project->first("TEMPLATE") == "subdirs") + return QString(); + + const QString root = "$(INSTALL_ROOT)"; + QStringList &uninst = project->values(t + ".uninstall"); + QString ret; + QString targetdir = Option::fixPathToTargetOS(project->first(t + ".path"), false); + targetdir = fileFixify(targetdir, FileFixifyAbsolute); + if(targetdir.right(1) != Option::dir_sep) + targetdir += Option::dir_sep; + + if(t == "target" && project->first("TEMPLATE") == "lib") { + if(project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") && + !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) { + QString dst_prl = Option::fixPathToTargetOS(project->first("QMAKE_INTERNAL_PRL_FILE")); + int slsh = dst_prl.lastIndexOf(Option::dir_sep); + if(slsh != -1) + dst_prl = dst_prl.right(dst_prl.length() - slsh - 1); + dst_prl = filePrefixRoot(root, targetdir + dst_prl); + ret += "-$(INSTALL_FILE) \"" + project->first("QMAKE_INTERNAL_PRL_FILE") + "\" \"" + dst_prl + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_prl + "\""); + } + if(project->isActiveConfig("shared") && !project->isActiveConfig("plugin")) { + QString lib_target = getLibTarget(); + lib_target.remove('"'); + QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + lib_target; + QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + lib_target, FileFixifyAbsolute)); + if(!ret.isEmpty()) + ret += "\n\t"; + ret += QString("-$(INSTALL_FILE)") + " \"" + src_targ + "\" \"" + dst_targ + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_targ + "\""); + } + } + + if(t == "dlltarget" || project->values(t + ".CONFIG").indexOf("no_dll") == -1) { + QString src_targ = "$(DESTDIR_TARGET)"; + QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + "$(TARGET)", FileFixifyAbsolute)); + if(!ret.isEmpty()) + ret += "\n\t"; + ret += QString("-$(INSTALL_FILE)") + " \"" + src_targ + "\" \"" + dst_targ + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_targ + "\""); + } + return ret; +} + +QString Win32MakefileGenerator::escapeFilePath(const QString &path) const +{ + QString ret = path; + if(!ret.isEmpty()) { + ret = unescapeFilePath(ret); + if(ret.contains(" ")) + ret = "\"" + ret + "\""; + debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData()); + } + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/winmakefile.h b/qmake/generators/win32/winmakefile.h new file mode 100644 index 0000000..c8e32f8 --- /dev/null +++ b/qmake/generators/win32/winmakefile.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINMAKEFILE_H +#define WINMAKEFILE_H + +#include "makefile.h" + +QT_BEGIN_NAMESPACE + +// In the Qt evaluation and educational version, we have a postfix in the +// library name (e.g. qtmteval301.dll). QTDLL_POSTFIX is used for this. +// A script modifies these lines when building eval/edu version, so be careful +// when changing them. +#ifndef QTDLL_POSTFIX +#define QTDLL_POSTFIX "" +#endif + +class Win32MakefileGenerator : public MakefileGenerator +{ +public: + Win32MakefileGenerator(); + ~Win32MakefileGenerator(); +protected: + virtual QString defaultInstall(const QString &); + virtual void writeCleanParts(QTextStream &t); + virtual void writeStandardParts(QTextStream &t); + virtual void writeIncPart(QTextStream &t); + virtual void writeLibDirPart(QTextStream &t); + virtual void writeLibsPart(QTextStream &t); + virtual void writeObjectsPart(QTextStream &t); + virtual void writeImplicitRulesPart(QTextStream &t); + virtual void writeBuildRulesPart(QTextStream &); + virtual QString escapeFilePath(const QString &path) const; + + virtual void writeRcFilePart(QTextStream &t); + + int findHighestVersion(const QString &dir, const QString &stem, const QString &ext = QLatin1String("lib")); + bool findLibraries(const QString &); + virtual bool findLibraries(); + + virtual void processPrlFiles(); + virtual void processVars(); + virtual void fixTargetExt(); + virtual void processRcFileVar(); + virtual void processFileTagsVar(); + virtual QString getLibTarget(); +}; + +inline Win32MakefileGenerator::~Win32MakefileGenerator() +{ } + +inline bool Win32MakefileGenerator::findLibraries() +{ return findLibraries("QMAKE_LIBS"); } + +QT_END_NAMESPACE + +#endif // WINMAKEFILE_H diff --git a/qmake/generators/xmloutput.cpp b/qmake/generators/xmloutput.cpp new file mode 100644 index 0000000..68d22e1 --- /dev/null +++ b/qmake/generators/xmloutput.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xmloutput.h" + +QT_BEGIN_NAMESPACE + +XmlOutput::XmlOutput(QTextStream &file, ConverstionType type) + : xmlFile(file), indent("\t"), currentLevel(0), currentState(Bare), format(NewLine), + conversion(type) +{ + tagStack.clear(); +} + +XmlOutput::~XmlOutput() +{ + closeAll(); +} + +// Settings ------------------------------------------------------------------ +void XmlOutput::setIndentString(const QString &indentString) +{ + indent = indentString; +} + +QString XmlOutput::indentString() +{ + return indent; +} + +void XmlOutput::setIndentLevel(int level) +{ + currentLevel = level; +} + +int XmlOutput::indentLevel() +{ + return currentLevel; +} + +void XmlOutput::setState(XMLState state) +{ + currentState = state; +} + +XmlOutput::XMLState XmlOutput::state() +{ + return currentState; +} + +void XmlOutput::updateIndent() +{ + currentIndent.clear(); + for (int i = 0; i < currentLevel; ++i) + currentIndent.append(indent); +} + +void XmlOutput::increaseIndent() +{ + ++currentLevel; + updateIndent(); +} + +void XmlOutput::decreaseIndent() +{ + if (currentLevel) + --currentLevel; + updateIndent(); + if (!currentLevel) + currentState = Bare; +} + +QString XmlOutput::doConversion(const QString &text) +{ + if (!text.count()) + return QString(); + else if (conversion == NoConversion) + return text; + + QString output; + if (conversion == XMLConversion) { + + // this is a way to escape characters that shouldn't be converted + for (int i=0; i<text.count(); ++i) { + if (text.at(i) == QLatin1Char('&')) { + if ( (i + 7) < text.count() && + text.at(i + 1) == QLatin1Char('#') && + text.at(i + 2) == QLatin1Char('x') && + text.at(i + 7) == QLatin1Char(';') ) { + output += text.at(i); + } else { + output += QLatin1String("&"); + } + } else { + QChar c = text.at(i); + if (c.unicode() < 0x20) { + output += QString("&#x%1;").arg(c.unicode(), 2, 16, QLatin1Char('0')); + } else { + output += text.at(i); + } + } + } + } else { + output = text; + } + + if (conversion == XMLConversion) { + output.replace('\"', """); + output.replace('\'', "'"); + } else if (conversion == EscapeConversion) { + output.replace('\"', "\\\""); + output.replace('\'', "\\\'"); + } + return output; +} + +// Stream functions ---------------------------------------------------------- +XmlOutput& XmlOutput::operator<<(const QString& o) +{ + return operator<<(data(o)); +} + +XmlOutput& XmlOutput::operator<<(const xml_output& o) +{ + switch(o.xo_type) { + case tNothing: + break; + case tRaw: + addRaw(o.xo_text); + break; + case tDeclaration: + addDeclaration(o.xo_text, o.xo_value); + break; + case tTag: + newTagOpen(o.xo_text); + break; + case tCloseTag: + if (o.xo_value.count()) + closeAll(); + else if (o.xo_text.count()) + closeTo(o.xo_text); + else + closeTag(); + break; + case tAttribute: + addAttribute(o.xo_text, o.xo_value); + break; + case tData: + { + // Special case to be able to close tag in normal + // way ("</tag>", not "/>") without using addRaw().. + if (!o.xo_text.count()) { + closeOpen(); + break; + } + QString output = doConversion(o.xo_text); + output.replace('\n', "\n" + currentIndent); + addRaw(QString("\n%1%2").arg(currentIndent).arg(output)); + } + break; + case tComment: + { + QString output("<!--%1-->"); + addRaw(output.arg(o.xo_text)); + } + break; + case tCDATA: + { + QString output("<![CDATA[\n%1\n]]>"); + addRaw(output.arg(o.xo_text)); + } + break; + } + return *this; +} + + +// Output functions ---------------------------------------------------------- +void XmlOutput::newTag(const QString &tag) +{ + Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag"); + newTagOpen(tag); + closeOpen(); +} + +void XmlOutput::newTagOpen(const QString &tag) +{ + Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag"); + closeOpen(); + + if (format == NewLine) + xmlFile << endl << currentIndent; + xmlFile << '<' << doConversion(tag); + currentState = Attribute; + tagStack.append(tag); + increaseIndent(); // ---> indent +} + +void XmlOutput::closeOpen() +{ + switch(currentState) { + case Bare: + case Tag: + return; + case Attribute: + break; + } + xmlFile << '>'; + currentState = Tag; +} + +void XmlOutput::closeTag() +{ + switch(currentState) { + case Bare: + if (tagStack.count()) + //warn_msg(WarnLogic, "<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count()); + qDebug("<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count()); + else + //warn_msg(WarnLogic, "<Root>: Cannot close tag, no tags on stack"); + qDebug("<Root>: Cannot close tag, no tags on stack"); + return; + case Tag: + decreaseIndent(); // <--- Pre-decrease indent + if (format == NewLine) + xmlFile << endl << currentIndent; + xmlFile << "</" << doConversion(tagStack.last()) << '>'; + tagStack.pop_back(); + break; + case Attribute: + xmlFile << "/>"; + tagStack.pop_back(); + currentState = Tag; + decreaseIndent(); // <--- Post-decrease indent + break; + } +} + +void XmlOutput::closeTo(const QString &tag) +{ + bool cont = true; + if (!tagStack.contains(tag) && tag != QString()) { + //warn_msg(WarnLogic, "<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().latin1(), tag.latin1()); + qDebug("<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().toLatin1().constData(), tag.toLatin1().constData()); + return; + } + int left = tagStack.count(); + while (left-- && cont) { + cont = tagStack.last().compare(tag) != 0; + closeTag(); + } +} + +void XmlOutput::closeAll() +{ + if (!tagStack.count()) + return; + closeTo(QString()); +} + +void XmlOutput::addDeclaration(const QString &version, const QString &encoding) +{ + switch(currentState) { + case Bare: + break; + case Tag: + case Attribute: + //warn_msg(WarnLogic, "<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData()); + qDebug("<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData()); + return; + } + QString outData = QString("<?xml version=\"%1\" encoding = \"%2\"?>") + .arg(doConversion(version)) + .arg(doConversion(encoding)); + addRaw(outData); +} + +void XmlOutput::addRaw(const QString &rawText) +{ + closeOpen(); + xmlFile << rawText; +} + +void XmlOutput::addAttribute(const QString &attribute, const QString &value) +{ + switch(currentState) { + case Bare: + case Tag: + //warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData()); + qDebug("<%s>: Cannot add attribute (%s) since tag's not open", + (tagStack.count() ? tagStack.last().toLatin1().constData() : "Root"), + attribute.toLatin1().constData()); + return; + case Attribute: + break; + } + if (format == NewLine) + xmlFile << endl; + xmlFile << currentIndent << doConversion(attribute) << "=\"" << doConversion(value) << "\""; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/xmloutput.h b/qmake/generators/xmloutput.h new file mode 100644 index 0000000..d0abc5e --- /dev/null +++ b/qmake/generators/xmloutput.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XMLOUTPUT_H +#define XMLOUTPUT_H + +#include <qtextstream.h> +#include <qstack.h> + +QT_BEGIN_NAMESPACE + +class XmlOutput +{ +public: + enum ConverstionType { + NoConversion, // No change + EscapeConversion, // Use '\"' + XMLConversion // Use " + }; + enum XMLFormat { + NoNewLine, // No new lines, unless added manually + NewLine // All properties & tags indented on new lines + }; + enum XMLState { + Bare, // Not in tag or attribute + Tag, // <tagname attribute1="value" + Attribute // attribute2="value"> + }; + enum XMLType { + tNothing, // No XML output, and not state change + tRaw, // Raw text (no formating) + tDeclaration, // <?xml version="x.x" encoding="xxx"?> + tTag, // <tagname attribute1="value" + tCloseTag, // Closes an open tag + tAttribute, // attribute2="value"> + tData, // Tag data (formating done) + tComment, // <!-- Comment --> + tCDATA // <![CDATA[ ... ]]> + }; + + XmlOutput(QTextStream &file, ConverstionType type = XMLConversion); + ~XmlOutput(); + + // Settings + void setIndentString(const QString &indentString); + QString indentString(); + void setIndentLevel(int level); + int indentLevel(); + void setState(XMLState state); + XMLState state(); + + + struct xml_output { + XMLType xo_type; // Type of struct instance + QString xo_text; // Tag/Attribute name/xml version + QString xo_value; // Value of attributes/xml encoding + + xml_output(XMLType type, const QString &text, const QString &value) + : xo_type(type), xo_text(text), xo_value(value) {} + xml_output(const xml_output &xo) + : xo_type(xo.xo_type), xo_text(xo.xo_text), xo_value(xo.xo_value) {} + }; + + // Streams + XmlOutput& operator<<(const QString& o); + XmlOutput& operator<<(const xml_output& o); + +private: + void increaseIndent(); + void decreaseIndent(); + void updateIndent(); + + QString doConversion(const QString &text); + + // Output functions + void newTag(const QString &tag); + void newTagOpen(const QString &tag); + void closeOpen(); + void closeTag(); + void closeTo(const QString &tag); + void closeAll(); + + void addDeclaration(const QString &version, const QString &encoding); + void addRaw(const QString &rawText); + void addAttribute(const QString &attribute, const QString &value); + void addData(const QString &data); + + // Data + QTextStream &xmlFile; + QString indent; + + QString currentIndent; + int currentLevel; + XMLState currentState; + + XMLFormat format; + ConverstionType conversion; + QStack<QString> tagStack; +}; + +inline XmlOutput::xml_output noxml() +{ + return XmlOutput::xml_output(XmlOutput::tNothing, QString(), QString()); +} + +inline XmlOutput::xml_output raw(const QString &rawText) +{ + return XmlOutput::xml_output(XmlOutput::tRaw, rawText, QString()); +} + +inline XmlOutput::xml_output declaration(const QString &version = QString("1.0"), + const QString &encoding = QString()) +{ + return XmlOutput::xml_output(XmlOutput::tDeclaration, version, encoding); +} + +inline XmlOutput::xml_output decl(const QString &version = QString("1.0"), + const QString &encoding = QString()) +{ + return declaration(version, encoding); +} + +inline XmlOutput::xml_output tag(const QString &name) +{ + return XmlOutput::xml_output(XmlOutput::tTag, name, QString()); +} + +inline XmlOutput::xml_output closetag() +{ + return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString()); +} + +inline XmlOutput::xml_output closetag(const QString &toTag) +{ + return XmlOutput::xml_output(XmlOutput::tCloseTag, toTag, QString()); +} + +inline XmlOutput::xml_output closeall() +{ + return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString("all")); +} + +inline XmlOutput::xml_output attribute(const QString &name, + const QString &value) +{ + return XmlOutput::xml_output(XmlOutput::tAttribute, name, value); +} + +inline XmlOutput::xml_output attr(const QString &name, + const QString &value) +{ + return attribute(name, value); +} + +inline XmlOutput::xml_output data(const QString &text = QString()) +{ + return XmlOutput::xml_output(XmlOutput::tData, text, QString()); +} + +inline XmlOutput::xml_output comment(const QString &text) +{ + return XmlOutput::xml_output(XmlOutput::tComment, text, QString()); +} + +inline XmlOutput::xml_output cdata(const QString &text) +{ + return XmlOutput::xml_output(XmlOutput::tCDATA, text, QString()); +} + +QT_END_NAMESPACE + +#endif // XMLOUTPUT_H |