/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the qmake application of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "pbuilder_pbx.h" #include "option.h" #include "meta.h" #include #include #include #include #include #include #ifdef Q_OS_UNIX # include # include #endif #ifdef Q_OS_DARWIN #include #include #endif QT_BEGIN_NAMESPACE //#define GENERATE_AGGREGRATE_SUBDIR // Note: this is fairly hacky, but it does the job... using namespace QMakeInternal; 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 pb_subdirs; pb_subdirs.append(new ProjectBuilderSubDirs(project, QString(), false)); QString oldpwd = qmake_getpwd(); QMap groups; for(int pb_subdir = 0; pb_subdir < pb_subdirs.size(); ++pb_subdir) { ProjectBuilderSubDirs *pb = pb_subdirs[pb_subdir]; const ProStringList &subdirs = pb->project->values("SUBDIRS"); for(int subdir = 0; subdir < subdirs.count(); subdir++) { ProString tmpk = subdirs[subdir]; const ProKey fkey(tmpk + ".file"); if (!pb->project->isEmpty(fkey)) { tmpk = pb->project->first(fkey); } else { const ProKey skey(tmpk + ".subdir"); if (!pb->project->isEmpty(skey)) tmpk = pb->project->first(skey); } QString tmp = tmpk.toQString(); if(fileInfo(tmp).isRelative() && !pb->subdir.isEmpty()) { QString subdir = pb->subdir; if(!subdir.endsWith(Option::dir_sep)) subdir += Option::dir_sep; tmp = subdir + tmp; } QFileInfo fi(fileInfo(Option::fixPathToLocalOS(tmp, true))); if(fi.exists()) { if(fi.isDir()) { QString profile = tmp; if(!profile.endsWith(Option::dir_sep)) profile += Option::dir_sep; profile += fi.baseName() + Option::pro_ext; fi = QFileInfo(profile); } QMakeProject tmp_proj; QString dir = fi.path(), fn = fi.fileName(); if(!dir.isEmpty()) { if(!qmake_setpwd(dir)) fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData()); } if(tmp_proj.read(fn)) { if(tmp_proj.first("TEMPLATE") == "subdirs") { QMakeProject *pp = new QMakeProject(&tmp_proj); 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", "") << ";" << "\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(ProKey(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", "") << ";" << "\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::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\t" << writeSettings("sourceTree", "") << ";" << "\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 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").toQString()); { const ProStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS"); for(int i = 0; i < l.size(); ++i) { ProString name = l.at(i); const ProKey nkey(name + ".name"); if (!project->isEmpty(nkey)) name = project->first(nkey); const QString value = project->values(ProKey(name + ".value")).join(QString(Option::field_sep)); settings.insert(name.toQString(), value); } } QString name = (as_release ? "Release" : "Debug"); 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::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"; key = keyFor("QMAKE_SUBDIR_PBX_BUILDSTYLE_" + name); project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES").append(key); if (project->isActiveConfig("debug") != (bool)as_release) active_buildstyle = name; t << "\t\t" << key << " = {" << "\n" << "\t\t\t" << writeSettings("buildRules", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << "buildSettings = {" << "\n"; for(QMap::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"; } 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", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << "buildSettings = {" << "\n" << "\t\t\t\t" << writeSettings("PRODUCT_NAME", project->values("TARGET").first()) << ";" << "\n" << "\t\t\t" << "};" << "\n"; { ProStringList dependencies; const ProStringList &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", "") << ";" << "\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", ProStringList()) << ";" << "\n"; t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST")) << ";" << "\n"; t << "\t\t\t" << "projectReferences = (" << "\n"; { const ProStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS"); for(int i = 0; i < qmake_subdirs.count(); i++) { const ProString &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 ProStringList(), #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(ProKey(key)).toQStringList(); if(key == "QMAKE_INTERNAL_INCLUDED_FILES") { ret.prepend(project->projectFile()); 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").toQString()); return ret; } bool ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t) { ProStringList tmp; bool did_preprocess = false; //HEADER const int pbVersion = pbuilderVersion(); ProStringList buildConfigGroups; buildConfigGroups << "PROJECT" << "TARGET"; 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")) { QString mkfile = pbx_dir + Option::dir_sep + "qt_makeqmake.mak"; QFile mkf(mkfile); if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) { writingUnixMakefileGenerator = true; debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData()); QTextStream mkt(&mkf); writeHeader(mkt); mkt << "QMAKE = " << var("QMAKE_QMAKE") << endl; writeMakeQmake(mkt); mkt.flush(); mkf.close(); writingUnixMakefileGenerator = false; } QString phase_key = keyFor("QMAKE_PBX_MAKEQMAKE_BUILDPHASE"); mkfile = fileFixify(mkfile, qmake_getpwd()); project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); t << "\t\t" << phase_key << " = {" << "\n" << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("generatedFileNames", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("name", "Qt Qmake") << ";" << "\n" << "\t\t\t" << writeSettings("neededFileNames", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n" << "\t\t\t" << writeSettings("shellScript", "make -C " + IoUtils::shellQuoteUnix(qmake_getpwd()) + " -f " + IoUtils::shellQuoteUnix(mkfile)) << ";" << "\n" << "\t\t" << "};" << "\n"; } //DUMP SOURCES QMap groups; QList 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 ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { if (project->isEmpty(ProKey(*it + ".output"))) continue; ProString name = *it; const ProKey nkey(*it + ".name"); if (!project->isEmpty(nkey)) name = project->first(nkey); const ProStringList &inputs = project->values(ProKey(*it + ".input")); for(int input = 0; input < inputs.size(); ++input) { if (project->isEmpty(inputs.at(input).toKey())) continue; bool duplicate = false; bool isObj = project->values(ProKey(*it + ".CONFIG")).indexOf("no_link") == -1; if (!isObj) { for (int i = 0; i < sources.size(); ++i) { if (sources.at(i).keyName() == inputs.at(input)) { duplicate = true; break; } } } if (!duplicate) { const ProStringList &outputs = project->values(ProKey(*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).toQString(), true, QString("Sources [") + name + "]", (*it).toQString(), isObj)); } } } } for(int source = 0; source < sources.size(); ++source) { ProStringList &src_list = project->values(ProKey("QMAKE_PBX_" + sources.at(source).keyName())); ProStringList &root_group_list = project->values("QMAKE_PBX_GROUPS"); const 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" << "\t\t\t" << writeSettings("sourceTree", sourceTreeForFile(file)) << ";" << "\n"; 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", ProStringList(), 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; ProStringList &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::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\t" << writeSettings("sourceTree", "") << ";" << "\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 ProStringList &incs = project->values("INCLUDEPATH"); for (ProStringList::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")) { const ProStringList &yaccs = project->values("YACCSOURCES"); for (ProStringList::ConstIterator yit = yaccs.begin(); yit != yaccs.end(); ++yit) { QFileInfo fi(fileInfo((*yit).toQString())); mkt << " " << fi.path() << Option::dir_sep << fi.baseName() << Option::yacc_mod << Option::cpp_ext.first(); } } if(!project->isEmpty("LEXSOURCES")) { const ProStringList &lexs = project->values("LEXSOURCES"); for (ProStringList::ConstIterator lit = lexs.begin(); lit != lexs.end(); ++lit) { QFileInfo fi(fileInfo((*lit).toQString())); 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 ProStringList &compilers = project->values("QMAKE_EXTRA_COMPILERS"); for(int compiler = 0; compiler < compilers.size(); ++compiler) { const ProStringList &tmp_out = project->values(ProKey(compilers.at(compiler) + ".output")); if (tmp_out.isEmpty()) continue; const ProStringList &inputs = project->values(ProKey(compilers.at(compiler) + ".input")); for(int input = 0; input < inputs.size(); ++input) { const ProStringList &files = project->values(inputs.at(input).toKey()); if (files.isEmpty()) continue; for(int file = 0, added = 0; file < files.size(); ++file) { QString fn = files.at(file).toQString(); if (!verifyExtraCompiler(compilers.at(compiler), fn)) continue; if(added && !(added % 3)) mkt << "\\\n\t"; ++added; const QString file_name = fileFixify(fn, Option::output_dir, Option::output_dir); mkt << " " << replaceExtraCompilerVariables(tmp_out.first().toQString(), 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", ProStringList(), 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", "make -C " + IoUtils::shellQuoteUnix(qmake_getpwd()) + " -f " + IoUtils::shellQuoteUnix(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 ProStringList &libdirs = project->values("QMAKE_PBX_LIBPATHS"), &frameworkdirs = project->values("QMAKE_FRAMEWORKPATH"); static const char * const libs[] = { "QMAKE_LFLAGS", "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", 0 }; for (int i = 0; libs[i]; i++) { tmp = project->values(libs[i]); for(int x = 0; x < tmp.count();) { bool remove = false; QString library, name; ProString opt = tmp[x].trimmed(); if (opt.length() >= 2 && (opt.at(0) == '"' || opt.at(0) == '\'') && opt.endsWith(opt.at(0))) opt = opt.mid(1, opt.length()-2); if(opt.startsWith("-L")) { QString r = opt.mid(2).toQString(); 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.mid(2).toQString(); QString lib("lib" + name); for (ProStringList::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(project); 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.mid(2).toQString(); } else { if(x == tmp.count()-1) break; r = tmp[++x].toQString(); } if(!r.isEmpty()) { fixForOutput(r); frameworkdirs.append(r); } } else if(opt == "-framework") { if(x == tmp.count()-1) break; const ProString &framework = tmp[x+1]; ProStringList fdirs = frameworkdirs; fdirs << "/System/Library/Frameworks/" << "/Library/Frameworks/"; for(int fdir = 0; fdir < fdirs.count(); fdir++) { if(exists(fdirs[fdir] + QDir::separator() + framework + ".framework")) { remove = true; library = fdirs[fdir] + Option::dir_sep + framework + ".framework"; tmp.removeAt(x); break; } } } else if (!opt.startsWith('-')) { QString fn = opt.toQString(); if (exists(fn)) { remove = true; library = fn; } } 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\t" << writeSettings("sourceTree", sourceTreeForFile(library)) << ";" << "\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(ProKey("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", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("generatedFileNames", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("name", "Qt Sublibs") << ";" << "\n" << "\t\t\t" << writeSettings("neededFileNames", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << "\n" << "\t\t\t" << writeSettings("shellScript", "make -C " + IoUtils::shellQuoteUnix(qmake_getpwd()) + " -f " + IoUtils::shellQuoteUnix(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", ProStringList()) << ";" << "\n" << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\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")) { ProString icon = project->first("ICON"); if (icon.length() >= 2 && (icon.at(0) == '"' || icon.at(0) == '\'') && icon.endsWith(icon.at(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"; } if (!project->isEmpty("DESTDIR")) { QString phase_key = keyFor("QMAKE_PBX_TARGET_COPY_PHASE"); QString destDir = project->first("DESTDIR").toQString(); destDir = fixForOutput(destDir); destDir = fileInfo(Option::fixPathToLocalOS(destDir)).absoluteFilePath(); project->values("QMAKE_PBX_BUILDPHASES").append(phase_key); t << "\t\t" << phase_key << " = {\n" << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("name", "Project Copy") << ";" << "\n" << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("inputPaths", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("outputPaths", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n" << "\t\t\t" << writeSettings("shellScript", fixForOutput("cp -r $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME " + escapeFilePath(destDir))) << ";" << "\n" << "\t\t" << "};\n"; } //BUNDLE_DATA BUILDPHASE (copy) if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { ProStringList bundle_file_refs; //all bundle data const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); for(int i = 0; i < bundle_data.count(); i++) { ProStringList pbx_files; //all files const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files")); for(int file = 0; file < files.count(); file++) { QString fn = files[file].toQString(); QString file_ref_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE_REF." + bundle_data[i] + "-" + fn); 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(fn)) << ";" << "\n" << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(fn)), SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("sourceTree", sourceTreeForFile(fn)) << ";" << "\n" << "\t\t" << "};" << "\n"; QString copy_file_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE." + bundle_data[i] + "-" + fn); 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(ProKey(bundle_data[i] + ".version"))) { //### } path += project->first(ProKey(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", ProStringList()) << ";" << "\n" << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\n" << "\t\t" << "};" << "\n"; } //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", ProStringList()) << ";" << "\n" << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\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") { ProString 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 { ProString 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\t" << writeSettings("sourceTree", "") << ";" << "\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"; ProString 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("LEXFLAGS", fixListForOutput("QMAKE_LEXFLAGS")) << ";" << "\n" << "\t\t\t\t" << writeSettings("YACCFLAGS", fixListForOutput("QMAKE_YACCFLAGS")) << ";" << "\n" << "\t\t\t\t" << writeSettings("OTHER_REZFLAGS", ProStringList()) << ";" << "\n" << "\t\t\t\t" << writeSettings("SECTORDER_FLAGS", ProStringList()) << ";" << "\n" << "\t\t\t\t" << writeSettings("WARNING_CFLAGS", ProStringList()) << ";" << "\n" << "\t\t\t\t" << writeSettings("PREBINDING", ProStringList((project->isEmpty("QMAKE_DO_PREBINDING") ? "NO" : "YES")), SettingsNoQuote) << ";" << "\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").toQString()); 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").toQString().section(Option::dir_sep, -1))); if(project->first("TEMPLATE") == "app") { plist_in_text = plist_in_text.replace("@EXECUTABLE@", project->first("QMAKE_ORIG_TARGET").toQString()); } else { plist_in_text = plist_in_text.replace("@LIBRARY@", project->first("QMAKE_ORIG_TARGET").toQString()); } if (!project->values("VERSION").isEmpty()) { plist_in_text = plist_in_text.replace("@SHORT_VERSION@", project->first("VER_MAJ") + "." + project->first("VER_MIN")); } plist_in_text = plist_in_text.replace("@TYPEINFO@", (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4).toQString())); 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_LIBS") + fixListForOutput("QMAKE_LIBS_PRIVATE"), SettingsAsList, 6) << ";" << "\n"; } if(!project->isEmpty("DESTDIR")) { ProString dir = project->first("DESTDIR"); if (QDir::isRelativePath(dir.toQString())) 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", ProStringList()) << ";" << "\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_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(project->first("TEMPLATE") == "app") { t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET").toQString())) << ";" << "\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"; } ProString 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].toQString(), val = QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())); 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"; t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST_TARGET"), SettingsNoQuote) << ";" << "\n"; t << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";" << "\n"; if(project->first("TEMPLATE") == "app") { if(!project->isActiveConfig("app_bundle")) { 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 { 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"; 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 << "" << "\n" << "\t\t\t\t" << "" << "\n" << "\t\t\t\t" << "" << "\n" << "\t\t\t\t" << "" << "\n" << "\t\t\t\t\t" << "CFBundleDevelopmentRegion" << "\n" << "\t\t\t\t\t" << "English" << "\n" << "\t\t\t\t\t" << "CFBundleExecutable" << "\n" << "\t\t\t\t\t" << "" << project->first("QMAKE_ORIG_TARGET") << "" << "\n" << "\t\t\t\t\t" << "CFBundleIconFile" << "\n" << "\t\t\t\t\t" << "" << var("ICON").section(Option::dir_sep, -1) << "" << "\n" << "\t\t\t\t\t" << "CFBundleInfoDictionaryVersion" << "\n" << "\t\t\t\t\t" << "6.0" << "\n" << "\t\t\t\t\t" << "CFBundlePackageType" << "\n" << "\t\t\t\t\t" << "APPL" << "\n" << "\t\t\t\t\t" << "CFBundleSignature" << "\n" << "\t\t\t\t\t" << "" << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << "" << "\n" << "\t\t\t\t\t" << "CFBundleVersion" << "\n" << "\t\t\t\t\t" << "0.1" << "\n" << "\t\t\t\t\t" << "CSResourcesFileMapped" << "\n" << "\t\t\t\t\t" << "" << "\n" << "\t\t\t\t" << "" << "\n" << "\t\t\t\t" << ""; } 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 { ProString 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 (!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"; } t << "\t\t\t" << writeSettings("startupPath", "<>") << ";" << "\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 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").toQString()); { const ProStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS"); for(int i = 0; i < l.size(); ++i) { ProString name = l.at(i); const QString value = project->values(ProKey(name + ".value")).join(QString(Option::field_sep)); const ProKey nkey(name + ".name"); if (!project->isEmpty(nkey)) name = project->values(nkey).first(); settings.insert(name.toQString(), value); } } if (project->first("TEMPLATE") == "app") { settings.insert("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET").toQString())); } else { ProString lib = project->first("QMAKE_ORIG_TARGET"); if (!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib")) lib.prepend("lib"); settings.insert("PRODUCT_NAME", escapeFilePath(lib.toQString())); } QString name = (as_release ? "Release" : "Debug"); for (int i = 0; i < buildConfigGroups.size(); i++) { QString key = keyFor("QMAKE_PBX_BUILDCONFIG_" + name + buildConfigGroups.at(i)); project->values(ProKey("QMAKE_PBX_BUILDCONFIGS_" + buildConfigGroups.at(i))).append(key); t << "\t\t" << key << " = {" << "\n" << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << "buildSettings = {" << "\n"; for (QMap::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it) t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n"; if (!project->isEmpty("PRECOMPILED_HEADER")) { 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"; } if (buildConfigGroups.at(i) == QLatin1String("PROJECT")) { t << "\t\t\t\t" << writeSettings("HEADER_SEARCH_PATHS", fixListForOutput("INCLUDEPATH") + ProStringList(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("FRAMEWORK_SEARCH_PATHS", fixListForOutput("QMAKE_FRAMEWORKPATH"), SettingsAsList, 5) << ";" << "\n" << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", "Info.plist") << ";" << "\n"; { ProStringList cflags = fixListForOutput("QMAKE_CFLAGS"); const ProStringList &prl_defines = project->values("PRL_EXPORT_DEFINES"); for (int i = 0; i < prl_defines.size(); ++i) cflags += "-D" + prl_defines.at(i); const ProStringList &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"; } { ProStringList cxxflags = fixListForOutput("QMAKE_CXXFLAGS"); const ProStringList &prl_defines = project->values("PRL_EXPORT_DEFINES"); for (int i = 0; i < prl_defines.size(); ++i) cxxflags += "-D" + prl_defines.at(i); const ProStringList &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"; } if (!project->isActiveConfig("staticlib")) { t << "\t\t\t\t" << writeSettings("OTHER_LDFLAGS", fixListForOutput("SUBLIBS") + fixListForOutput("QMAKE_LFLAGS") + fixListForOutput("QMAKE_LIBS") + fixListForOutput("QMAKE_LIBS_PRIVATE"), SettingsAsList, 6) << ";" << "\n"; } const ProStringList &archs = project->values("QT_ARCH"); if (!archs.isEmpty()) t << "\t\t\t\t" << writeSettings("ARCHS", archs) << ";" << "\n"; if (!project->isEmpty("OBJECTS_DIR")) t << "\t\t\t\t" << writeSettings("OBJROOT", escapeFilePath(project->first("OBJECTS_DIR").toQString())) << ";" << "\n"; } else { if (project->first("TEMPLATE") == "app") { t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET").toQString())) << ";" << "\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"; ProString 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"; } } t << "\t\t\t" << "};" << "\n" << "\t\t\t" << writeSettings("name", name) << ";" << "\n" << "\t\t" << "};" << "\n"; key = keyFor("QMAKE_PBX_BUILDSTYLE_" + name); project->values("QMAKE_PBX_BUILDSTYLES").append(key); if (project->isActiveConfig("debug") != (bool)as_release) active_buildstyle = name; t << "\t\t" << key << " = {" << "\n" << "\t\t\t" << writeSettings("buildRules", ProStringList(), SettingsAsList, 4) << ";" << "\n" << "\t\t\t" << "buildSettings = {" << "\n"; for(QMap::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"; } } for (int i = 0; i < buildConfigGroups.size(); i++) { t << "\t\t" << keyFor("QMAKE_PBX_BUILDCONFIG_LIST_" + buildConfigGroups.at(i)) << " = {" << "\n" << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";" << "\n" << "\t\t\t" << writeSettings("buildConfigurations", project->values(ProKey("QMAKE_PBX_BUILDCONFIGS_" + buildConfigGroups.at(i))), 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"; t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST_PROJECT")) << ";" << "\n"; t << "\t\t\t" << writeSettings("projectDirPath", ProStringList()) << ";" << "\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 ProString &prog) { QString ret = prog.toQString(); 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; } ProStringList ProjectBuilderMakefileGenerator::fixListForOutput(const char *where) { ProStringList ret; const ProStringList &l = project->values(where); for(int i = 0; i < l.count(); i++) ret += fixForOutput(l[i].toQString()); 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").toQString(); } 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").toQString(); } else { QString version, version_plist = project->first("QMAKE_PBUILDER_VERSION_PLIST").toQString(); if(version_plist.isEmpty()) { #ifdef Q_OS_DARWIN ret = QLatin1String("34"); QCFType cfurl; // Check for XCode 4 first OSStatus err = LSFindApplicationForInfo(0, CFSTR("com.apple.dt.Xcode"), 0, 0, &cfurl); // Now check for XCode 3 if (err == kLSApplicationNotFoundErr) err = LSFindApplicationForInfo(0, CFSTR("com.apple.Xcode"), 0, 0, &cfurl); if (err == noErr) { QCFType 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 >= 3) { ret = QLatin1String("46"); } else 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("^(.*)$"), stringreg("^(.*)$"); while(!plist.atEnd()) { QString line = plist.readLine().trimmed(); if(line == "") in_dict = true; else if(line == "") 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); if (int_ret < 46) warn_msg(WarnLogic, "XCode version is too old, at least XCode 3.2 is required"); return int_ret; } } debug_msg(1, "pbuilder: version.plist: Fallback to default version"); return 46; //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::sourceTreeForFile(const QString &where) { QString ret = ""; if (QDir::isRelativePath(unescapeFilePath(where))) ret = "SOURCE_ROOT"; //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(const QString &var, const ProStringList &vals, int flags, int indent_level) { QString ret; const QString quote = (flags & SettingsNoQuote) ? "" : "\""; const QString escape_quote = quote.isEmpty() ? "" : QString("\\" + 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).toQString(); 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).toQString(); // if(val.isEmpty()) // val = quote + quote; if(i) ret += " "; ret += val; } ret += quote; } return ret; } QT_END_NAMESPACE