1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the qmake application of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "pbuilder_pbx.h"
30 #include "option.h"
31 #include "meta.h"
32 #include <qdir.h>
33 #include <qregexp.h>
34 #include <qcryptographichash.h>
35 #include <qdebug.h>
36 #include <qsettings.h>
37 #include <qstring.h>
38 #include <stdlib.h>
39 #include <time.h>
40 #ifdef Q_OS_UNIX
41 # include <sys/types.h>
42 # include <sys/stat.h>
43 #endif
44 #ifdef Q_OS_DARWIN
45 #include <ApplicationServices/ApplicationServices.h>
46 #include <private/qcore_mac_p.h>
47 #endif
48
49 QT_BEGIN_NAMESPACE
50
51 //#define GENERATE_AGGREGRATE_SUBDIR
52
53 // Note: this is fairly hacky, but it does the job...
54
55 using namespace QMakeInternal;
56
qtSha1(const QByteArray & src)57 static QString qtSha1(const QByteArray &src)
58 {
59 QByteArray digest = QCryptographicHash::hash(src, QCryptographicHash::Sha1);
60 return QString::fromLatin1(digest.toHex());
61 }
62
63 bool
writeMakefile(QTextStream & t)64 ProjectBuilderMakefileGenerator::writeMakefile(QTextStream &t)
65 {
66 writingUnixMakefileGenerator = false;
67 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) {
68 /* for now just dump, I need to generated an empty xml or something.. */
69 fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n",
70 var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData());
71 return true;
72 }
73
74 project->values("MAKEFILE").clear();
75 project->values("MAKEFILE").append("Makefile");
76 if(project->first("TEMPLATE") == "app" || project->first("TEMPLATE") == "lib")
77 return writeMakeParts(t);
78 else if(project->first("TEMPLATE") == "subdirs")
79 return writeSubDirs(t);
80 return false;
81 }
82
83 struct ProjectBuilderSubDirs {
84 QMakeProject *project;
85 QString subdir;
86 bool autoDelete;
ProjectBuilderSubDirsProjectBuilderSubDirs87 ProjectBuilderSubDirs(QMakeProject *p, QString s, bool a=true) : project(p), subdir(s), autoDelete(a) { }
~ProjectBuilderSubDirsProjectBuilderSubDirs88 ~ProjectBuilderSubDirs() {
89 if(autoDelete)
90 delete project;
91 }
92 };
93
94 bool
writeSubDirs(QTextStream & t)95 ProjectBuilderMakefileGenerator::writeSubDirs(QTextStream &t)
96 {
97 //HEADER
98 const int pbVersion = pbuilderVersion();
99 t << "// !$*UTF8*$!\n"
100 << "{\n"
101 << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";\n"
102 << "\tclasses = {\n\t};\n"
103 << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";\n"
104 << "\tobjects = {\n";
105
106 //SUBDIRS
107 QList<ProjectBuilderSubDirs*> pb_subdirs;
108 pb_subdirs.append(new ProjectBuilderSubDirs(project, QString(), false));
109 QString oldpwd = qmake_getpwd();
110 QString oldoutpwd = Option::output_dir;
111 QMap<QString, ProStringList> groups;
112 for(int pb_subdir = 0; pb_subdir < pb_subdirs.size(); ++pb_subdir) {
113 ProjectBuilderSubDirs *pb = pb_subdirs[pb_subdir];
114 const ProStringList &subdirs = pb->project->values("SUBDIRS");
115 for(int subdir = 0; subdir < subdirs.count(); subdir++) {
116 ProString tmpk = subdirs[subdir];
117 const ProKey fkey(tmpk + ".file");
118 if (!pb->project->isEmpty(fkey)) {
119 tmpk = pb->project->first(fkey);
120 } else {
121 const ProKey skey(tmpk + ".subdir");
122 if (!pb->project->isEmpty(skey))
123 tmpk = pb->project->first(skey);
124 }
125 QString tmp = tmpk.toQString();
126 if(fileInfo(tmp).isRelative() && !pb->subdir.isEmpty()) {
127 QString subdir = pb->subdir;
128 if(!subdir.endsWith(Option::dir_sep))
129 subdir += Option::dir_sep;
130 tmp = subdir + tmp;
131 }
132 QFileInfo fi(fileInfo(Option::normalizePath(tmp)));
133 if(fi.exists()) {
134 if(fi.isDir()) {
135 QString profile = tmp;
136 if(!profile.endsWith(Option::dir_sep))
137 profile += Option::dir_sep;
138 profile += fi.baseName() + Option::pro_ext;
139 fi = QFileInfo(profile);
140 }
141 QMakeProject tmp_proj;
142 QString dir = fi.path(), fn = fi.fileName();
143 if(!dir.isEmpty()) {
144 if(!qmake_setpwd(dir))
145 fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData());
146 }
147 Option::output_dir = Option::globals->shadowedPath(qmake_getpwd());
148 if(tmp_proj.read(fn)) {
149 if(tmp_proj.first("TEMPLATE") == "subdirs") {
150 QMakeProject *pp = new QMakeProject(&tmp_proj);
151 pb_subdirs += new ProjectBuilderSubDirs(pp, dir);
152 } else if(tmp_proj.first("TEMPLATE") == "app" || tmp_proj.first("TEMPLATE") == "lib") {
153 QString pbxproj = Option::output_dir + Option::dir_sep + tmp_proj.first("TARGET") + projectSuffix();
154 if(!exists(pbxproj)) {
155 warn_msg(WarnLogic, "Ignored (not found) '%s'", pbxproj.toLatin1().constData());
156 goto nextfile; // # Dirty!
157 }
158 const QString project_key = keyFor(pbxproj + "_PROJECTREF");
159 project->values("QMAKE_PBX_SUBDIRS") += pbxproj;
160 //PROJECTREF
161 {
162 bool in_root = true;
163 QString name = qmake_getpwd();
164 if(project->isActiveConfig("flat")) {
165 QString flat_file = fileFixify(name, FileFixifyBackwards | FileFixifyRelative);
166 if(flat_file.indexOf(Option::dir_sep) != -1) {
167 QStringList dirs = flat_file.split(Option::dir_sep);
168 name = dirs.back();
169 }
170 } else {
171 QString flat_file = fileFixify(name, FileFixifyBackwards | FileFixifyRelative);
172 if(QDir::isRelativePath(flat_file) && flat_file.indexOf(Option::dir_sep) != -1) {
173 QString last_grp("QMAKE_SUBDIR_PBX_HEIR_GROUP");
174 QStringList dirs = flat_file.split(Option::dir_sep);
175 name = dirs.back();
176 for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
177 QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp));
178 if(dir_it == dirs.begin()) {
179 if(!groups.contains(new_grp))
180 project->values("QMAKE_SUBDIR_PBX_GROUPS").append(new_grp_key);
181 } else {
182 if(!groups[last_grp].contains(new_grp_key))
183 groups[last_grp] += new_grp_key;
184 }
185 last_grp = new_grp;
186 }
187 groups[last_grp] += project_key;
188 in_root = false;
189 }
190 }
191 if(in_root)
192 project->values("QMAKE_SUBDIR_PBX_GROUPS") += project_key;
193 t << "\t\t" << project_key << " = {\n"
194 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
195 << "\t\t\t" << writeSettings("lastKnownFileType", "wrapper.pb-project") << ";\n"
196 << "\t\t\t" << writeSettings("name", tmp_proj.first("TARGET") + projectSuffix()) << ";\n"
197 << "\t\t\t" << writeSettings("path", pbxproj) << ";\n"
198 << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";\n"
199 << "\t\t};\n";
200 //WRAPPER
201 t << "\t\t" << keyFor(pbxproj + "_WRAPPER") << " = {\n"
202 << "\t\t\t" << writeSettings("isa", "PBXReferenceProxy", SettingsNoQuote) << ";\n";
203 if(tmp_proj.first("TEMPLATE") == "app") {
204 t << "\t\t\t" << writeSettings("fileType", "wrapper.application") << ";\n"
205 << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".app") << ";\n";
206 } else {
207 t << "\t\t\t" << writeSettings("fileType", "compiled.mach-o.dylib") << ";\n"
208 << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".dylib") << ";\n";
209 }
210 t << "\t\t\t" << writeSettings("remoteRef", keyFor(pbxproj + "_WRAPPERREF")) << ";\n"
211 << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";\n"
212 << "\t\t};\n";
213 t << "\t\t" << keyFor(pbxproj + "_WRAPPERREF") << " = {\n"
214 << "\t\t\t" << writeSettings("containerPortal", project_key) << ";\n"
215 << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";\n"
216 << "\t\t\t" << writeSettings("proxyType", "2") << ";\n"
217 // << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE")) << ";\n"
218 << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE!!!")) << ";\n"
219 << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";\n"
220 << "\t\t};\n";
221 //PRODUCTGROUP
222 t << "\t\t" << keyFor(pbxproj + "_PRODUCTGROUP") << " = {\n"
223 << "\t\t\t" << writeSettings("children", project->values(ProKey(pbxproj + "_WRAPPER")), SettingsAsList, 4) << ";\n"
224 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
225 << "\t\t\t" << writeSettings("name", "Products") << ";\n"
226 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
227 << "\t\t};\n";
228 }
229 #ifdef GENERATE_AGGREGRATE_SUBDIR
230 //TARGET (for aggregate)
231 {
232 //container
233 const QString container_proxy = keyFor(pbxproj + "_CONTAINERPROXY");
234 t << "\t\t" << container_proxy << " = {\n"
235 << "\t\t\t" << writeSettings("containerPortal", project_key) << ";\n"
236 << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";\n"
237 << "\t\t\t" << writeSettings("proxyType", "1") << ";\n"
238 << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_TARGET")) << ";\n"
239 << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";\n"
240 << "\t\t};\n";
241 //targetref
242 t << "\t\t" << keyFor(pbxproj + "_TARGETREF") << " = {\n"
243 << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";\n"
244 << "\t\t\t" << writeSettings("name", fixForOutput(tmp_proj.first("TARGET") +" (from " + tmp_proj.first("TARGET") + projectSuffix() + ")")) << ";\n"
245 << "\t\t\t" << writeSettings("targetProxy", container_proxy) << ";\n"
246 << "\t\t};\n";
247 }
248 #endif
249 }
250 }
251 nextfile:
252 qmake_setpwd(oldpwd);
253 Option::output_dir = oldoutpwd;
254 }
255 }
256 }
257 qDeleteAll(pb_subdirs);
258 pb_subdirs.clear();
259
260 for (QMap<QString, ProStringList>::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) {
261 t << "\t\t" << keyFor(grp_it.key()) << " = {\n"
262 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
263 << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";\n"
264 << "\t\t\t" << writeSettings("name", grp_it.key().section(Option::dir_sep, -1)) << ";\n"
265 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
266 << "\t\t};\n";
267 }
268
269 //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER
270 //BUILDCONFIGURATIONS
271 QString defaultConfig;
272 for(int as_release = 0; as_release < 2; as_release++)
273 {
274 QString configName = (as_release ? "Release" : "Debug");
275
276 QMap<QString, QString> settings;
277 if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK"))
278 settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK").toQString());
279 {
280 const ProStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS");
281 for(int i = 0; i < l.size(); ++i) {
282 ProString name = l.at(i);
283 const ProKey buildKey(name + ".build");
284 if (!project->isEmpty(buildKey)) {
285 const QString build = project->first(buildKey).toQString();
286 if (build.toLower() != configName.toLower())
287 continue;
288 }
289 const ProKey nkey(name + ".name");
290 if (!project->isEmpty(nkey))
291 name = project->first(nkey);
292 const QString value = project->values(ProKey(name + ".value")).join(QString(Option::field_sep));
293 settings.insert(name.toQString(), value);
294 }
295 }
296
297 if (project->isActiveConfig("debug") != (bool)as_release)
298 defaultConfig = configName;
299 QString key = keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_" + configName);
300 project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS").append(key);
301 t << "\t\t" << key << " = {\n"
302 << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";\n"
303 << "\t\t\tbuildSettings = {\n";
304 for (QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
305 t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n";
306 t << "\t\t\t};\n"
307 << "\t\t\t" << writeSettings("name", configName) << ";\n"
308 << "\t\t};\n";
309 }
310 t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST") << " = {\n"
311 << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";\n"
312 << "\t\t\t" << writeSettings("buildConfigurations", project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS"), SettingsAsList, 4) << ";\n"
313 << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";\n"
314 << "\t\t\t" << writeSettings("defaultConfigurationName", defaultConfig, SettingsNoQuote) << ";\n"
315 << "\t\t};\n";
316
317 #ifdef GENERATE_AGGREGRATE_SUBDIR
318 //target
319 t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_AGGREGATE_TARGET") << " = {\n"
320 << "\t\t\t" << writeSettings("buildPhases", ProStringList(), SettingsAsList, 4) << ";\n"
321 << "\t\t\tbuildSettings = {\n"
322 << "\t\t\t\t" << writeSettings("PRODUCT_NAME", project->first("TARGET")) << ";\n"
323 << "\t\t\t};\n";
324 {
325 ProStringList dependencies;
326 const ProStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS");
327 for(int i = 0; i < qmake_subdirs.count(); i++)
328 dependencies += keyFor(qmake_subdirs[i] + "_TARGETREF");
329 t << "\t\t\t" << writeSettings("dependencies", dependencies, SettingsAsList, 4) << ";\n"
330 }
331 t << "\t\t\t" << writeSettings("isa", "PBXAggregateTarget", SettingsNoQuote) << ";\n"
332 << "\t\t\t" << writeSettings("name", project->first("TARGET")) << ";\n"
333 << "\t\t\t" << writeSettings("productName", project->first("TARGET")) << ";\n"
334 << "\t\t};\n";
335 #endif
336
337 //ROOT_GROUP
338 t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP") << " = {\n"
339 << "\t\t\t" << writeSettings("children", project->values("QMAKE_SUBDIR_PBX_GROUPS"), SettingsAsList, 4) << ";\n"
340 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
341 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
342 << "\t\t};\n";
343
344
345 //ROOT
346 t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT") << " = {\n"
347 << "\t\t\tbuildSettings = {\n"
348 << "\t\t\t};\n"
349 << "\t\t\t" << writeSettings("buildStyles", project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES"), SettingsAsList, 4) << ";\n"
350 << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";\n"
351 << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP")) << ";\n"
352 << "\t\t\t" << writeSettings("projectDirPath", ProStringList()) << ";\n";
353 t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST")) << ";\n";
354 t << "\t\t\tprojectReferences = (\n";
355 {
356 const ProStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS");
357 for(int i = 0; i < qmake_subdirs.count(); i++) {
358 const ProString &subdir = qmake_subdirs[i];
359 t << "\t\t\t\t{\n"
360 << "\t\t\t\t\t" << writeSettings("ProductGroup", keyFor(subdir + "_PRODUCTGROUP")) << ";\n"
361 << "\t\t\t\t\t" << writeSettings("ProjectRef", keyFor(subdir + "_PROJECTREF")) << ";\n"
362 << "\t\t\t\t},\n";
363 }
364 }
365 t << "\t\t\t);\n"
366 << "\t\t\t" << writeSettings("targets",
367 #ifdef GENERATE_AGGREGRATE_SUBDIR
368 project->values("QMAKE_SUBDIR_AGGREGATE_TARGET"),
369 #else
370 ProStringList(),
371 #endif
372 SettingsAsList, 4) << ";\n"
373 << "\t\t};\n";
374
375 //FOOTER
376 t << "\t};\n"
377 << "\t" << writeSettings("rootObject", keyFor("QMAKE_SUBDIR_PBX_ROOT")) << ";\n"
378 << "}\n";
379
380 return true;
381 }
382
383 class ProjectBuilderSources
384 {
385 bool buildable, object_output;
386 QString key, group, compiler;
387 public:
388 ProjectBuilderSources(const QString &key, bool buildable = false, const QString &compiler = QString(), bool producesObject = false);
389 QStringList files(QMakeProject *project) const;
isBuildable() const390 inline bool isBuildable() const { return buildable; }
keyName() const391 inline QString keyName() const { return key; }
groupName() const392 inline QString groupName() const { return group; }
compilerName() const393 inline QString compilerName() const { return compiler; }
isObjectOutput(const QString & file) const394 inline bool isObjectOutput(const QString &file) const {
395 if (object_output)
396 return true;
397
398 if (file.endsWith(Option::objc_ext))
399 return true;
400 if (file.endsWith(Option::objcpp_ext))
401 return true;
402
403 for (int i = 0; i < Option::c_ext.size(); ++i) {
404 if (file.endsWith(Option::c_ext.at(i)))
405 return true;
406 }
407 for (int i = 0; i < Option::cpp_ext.size(); ++i) {
408 if (file.endsWith(Option::cpp_ext.at(i)))
409 return true;
410 }
411
412 return false;
413 }
414 };
415
ProjectBuilderSources(const QString & k,bool b,const QString & c,bool o)416 ProjectBuilderSources::ProjectBuilderSources(const QString &k, bool b, const QString &c, bool o) :
417 buildable(b), object_output(o), key(k), compiler(c)
418 {
419 // Override group name for a few common keys
420 if (k == "SOURCES" || k == "OBJECTIVE_SOURCES" || k == "HEADERS")
421 group = "Sources";
422 else if (k == "QMAKE_INTERNAL_INCLUDED_FILES")
423 group = "Supporting Files";
424 else if (k == "GENERATED_SOURCES" || k == "GENERATED_FILES")
425 group = "Generated Sources";
426 else if (k == "RESOURCES")
427 group = "Resources";
428 else if (group.isNull())
429 group = QString("Sources [") + c + "]";
430 }
431
432 QStringList
files(QMakeProject * project) const433 ProjectBuilderSources::files(QMakeProject *project) const
434 {
435 QStringList ret = project->values(ProKey(key)).toQStringList();
436 if(key == "QMAKE_INTERNAL_INCLUDED_FILES") {
437 QString qtPrefix(project->propertyValue(ProKey("QT_INSTALL_PREFIX/get")).toQString() + '/');
438 QString qtSrcPrefix(project->propertyValue(ProKey("QT_INSTALL_PREFIX/src")).toQString() + '/');
439
440 QStringList newret;
441 for(int i = 0; i < ret.size(); ++i) {
442 // Don't show files "internal" to Qt in Xcode
443 if (ret.at(i).startsWith(qtPrefix) || ret.at(i).startsWith(qtSrcPrefix))
444 continue;
445
446 newret.append(ret.at(i));
447 }
448 ret = newret;
449 }
450 if(key == "SOURCES" && project->first("TEMPLATE") == "app" && !project->isEmpty("ICON"))
451 ret.append(project->first("ICON").toQString());
452 return ret;
453 }
454
xcodeFiletypeForFilename(const QString & filename)455 static QString xcodeFiletypeForFilename(const QString &filename)
456 {
457 for (const QString &ext : qAsConst(Option::cpp_ext)) {
458 if (filename.endsWith(ext))
459 return QStringLiteral("sourcecode.cpp.cpp");
460 }
461
462 for (const QString &ext : qAsConst(Option::c_ext)) {
463 if (filename.endsWith(ext))
464 return QStringLiteral("sourcecode.c.c");
465 }
466
467 for (const QString &ext : qAsConst(Option::h_ext)) {
468 if (filename.endsWith(ext))
469 return "sourcecode.c.h";
470 }
471
472 if (filename.endsWith(Option::objcpp_ext))
473 return QStringLiteral("sourcecode.cpp.objcpp");
474 if (filename.endsWith(Option::objc_ext))
475 return QStringLiteral("sourcecode.c.objc");
476 if (filename.endsWith(QLatin1String(".framework")))
477 return QStringLiteral("wrapper.framework");
478 if (filename.endsWith(QLatin1String(".a")))
479 return QStringLiteral("archive.ar");
480 if (filename.endsWith(QLatin1String(".pro")) || filename.endsWith(QLatin1String(".qrc")))
481 return QStringLiteral("text");
482
483 return QString();
484 }
485
compareProvisioningTeams(const QVariantMap & a,const QVariantMap & b)486 static bool compareProvisioningTeams(const QVariantMap &a, const QVariantMap &b)
487 {
488 int aFree = a.value(QLatin1String("isFreeProvisioningTeam")).toBool() ? 1 : 0;
489 int bFree = b.value(QLatin1String("isFreeProvisioningTeam")).toBool() ? 1 : 0;
490 return aFree < bFree;
491 }
492
provisioningTeams()493 static QList<QVariantMap> provisioningTeams()
494 {
495 const QSettings xcodeSettings(
496 QDir::homePath() + QLatin1String("/Library/Preferences/com.apple.dt.Xcode.plist"),
497 QSettings::NativeFormat);
498 const QVariantMap teamMap = xcodeSettings.value(QLatin1String("IDEProvisioningTeams")).toMap();
499 QList<QVariantMap> flatTeams;
500 for (QVariantMap::const_iterator it = teamMap.begin(), end = teamMap.end(); it != end; ++it) {
501 const QString emailAddress = it.key();
502 QVariantMap team = it.value().toMap();
503 team[QLatin1String("emailAddress")] = emailAddress;
504 flatTeams.append(team);
505 }
506
507 // Sort teams so that Free Provisioning teams come last
508 std::sort(flatTeams.begin(), flatTeams.end(), ::compareProvisioningTeams);
509 return flatTeams;
510 }
511
replaceLibrarySuffix(const QString & lib_file,const ProString & opt,QString & name,QString & library)512 bool ProjectBuilderMakefileGenerator::replaceLibrarySuffix(const QString &lib_file,
513 const ProString &opt,
514 QString &name, QString &library)
515 {
516 /* This isn't real nice, but it is real useful. This looks in a prl
517 for what the library will ultimately be called so we can stick it
518 in the ProjectFile. If the prl format ever changes (not likely) then
519 this will not really work. However, more concerning is that it will
520 encode the version number in the Project file which might be a bad
521 things in days to come? --Sam
522 */
523 if (lib_file.isEmpty())
524 return false;
525
526 QMakeMetaInfo libinfo;
527 if (!libinfo.readLib(lib_file) || libinfo.isEmpty("QMAKE_PRL_TARGET"))
528 return false;
529
530 const QString libDir = fileInfo(lib_file).absolutePath();
531 library = libDir + Option::dir_sep + libinfo.first("QMAKE_PRL_TARGET");
532
533 debug_msg(1, "pbuilder: Found library (%s) via PRL %s (%s)",
534 opt.toLatin1().constData(), lib_file.toLatin1().constData(), library.toLatin1().constData());
535
536 if (project->isActiveConfig("xcode_dynamic_library_suffix")) {
537 QString suffixSetting = project->first("QMAKE_XCODE_LIBRARY_SUFFIX_SETTING").toQString();
538 if (!suffixSetting.isEmpty()) {
539 QString librarySuffix = project->first("QMAKE_XCODE_LIBRARY_SUFFIX").toQString();
540 suffixSetting = "$(" + suffixSetting + ")";
541 if (!librarySuffix.isEmpty()) {
542 int pos = library.lastIndexOf(librarySuffix + '.');
543 if (pos == -1) {
544 warn_msg(WarnLogic, "Failed to find expected suffix '%s' for library '%s'.",
545 qPrintable(librarySuffix), qPrintable(library));
546 } else {
547 library.replace(pos, librarySuffix.length(), suffixSetting);
548 if (name.endsWith(librarySuffix))
549 name.chop(librarySuffix.length());
550 }
551 } else {
552 int pos = library.lastIndexOf(name);
553 if (pos != -1)
554 library.insert(pos + name.length(), suffixSetting);
555 }
556 }
557 }
558
559 return true;
560 }
561
562 bool
writeMakeParts(QTextStream & t)563 ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
564 {
565 ProStringList tmp;
566
567 //HEADER
568 const int pbVersion = pbuilderVersion();
569 ProStringList buildConfigGroups;
570 buildConfigGroups << "PROJECT" << "TARGET";
571
572 t << "// !$*UTF8*$!\n"
573 << "{\n"
574 << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";\n"
575 << "\tclasses = {\n\t};\n"
576 << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";\n"
577 << "\tobjects = {\n";
578
579 //MAKE QMAKE equivelant
580 if (!project->isActiveConfig("no_autoqmake")) {
581 QString mkfile = pbx_dir + Option::dir_sep + "qt_makeqmake.mak";
582 QFile mkf(mkfile);
583 if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
584 writingUnixMakefileGenerator = true;
585 debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
586 QTextStream mkt(&mkf);
587 writeHeader(mkt);
588 mkt << "QMAKE = " << var("QMAKE_QMAKE") << Qt::endl;
589 project->values("QMAKE_MAKE_QMAKE_EXTRA_COMMANDS")
590 << "@echo 'warning: Xcode project has been regenerated, custom settings have been lost. " \
591 "Use CONFIG+=no_autoqmake to prevent this behavior in the future, " \
592 "at the cost of requiring manual project change tracking.'";
593 writeMakeQmake(mkt);
594 mkt.flush();
595 mkf.close();
596 writingUnixMakefileGenerator = false;
597 }
598 QString phase_key = keyFor("QMAKE_PBX_MAKEQMAKE_BUILDPHASE");
599 mkfile = fileFixify(mkfile);
600 project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
601 t << "\t\t" << phase_key << " = {\n"
602 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
603 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
604 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
605 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
606 << "\t\t\t" << writeSettings("name", "Qt Qmake") << ";\n"
607 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";\n"
608 << "\t\t\t" << writeSettings("shellScript", "make -C " + IoUtils::shellQuoteUnix(Option::output_dir)
609 + " -f " + IoUtils::shellQuoteUnix(mkfile)) << ";\n"
610 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
611 << "\t\t};\n";
612 }
613
614 // FIXME: Move all file resolving logic out of ProjectBuilderSources::files(), as it
615 // doesn't have access to any of the information it needs to resolve relative paths.
616 project->values("QMAKE_INTERNAL_INCLUDED_FILES").prepend(project->projectFile());
617
618 // Since we can't fileFixify inside ProjectBuilderSources::files(), we resolve the absolute paths here
619 project->values("QMAKE_INTERNAL_INCLUDED_FILES") = ProStringList(
620 fileFixify(project->values("QMAKE_INTERNAL_INCLUDED_FILES").toQStringList(),
621 FileFixifyFromOutdir | FileFixifyAbsolute));
622
623 //DUMP SOURCES
624 QSet<QString> processedSources;
625 QMap<QString, ProStringList> groups;
626 QList<ProjectBuilderSources> sources;
627 sources.append(ProjectBuilderSources("SOURCES", true));
628 sources.append(ProjectBuilderSources("GENERATED_SOURCES", true));
629 sources.append(ProjectBuilderSources("GENERATED_FILES"));
630 sources.append(ProjectBuilderSources("HEADERS"));
631 if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
632 const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
633 for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
634 if (project->isEmpty(ProKey(*it + ".output")))
635 continue;
636 ProStringList &inputs = project->values(ProKey(*it + ".input"));
637 int input = 0;
638 while (input < inputs.size()) {
639 if (project->isEmpty(inputs.at(input).toKey())) {
640 ++input;
641 continue;
642 }
643 bool duplicate = false;
644 bool isObj = project->values(ProKey(*it + ".CONFIG")).indexOf("no_link") == -1;
645 if (!isObj) {
646 for (int i = 0; i < sources.size(); ++i) {
647 if (sources.at(i).keyName() == inputs.at(input)) {
648 duplicate = true;
649 break;
650 }
651 }
652 }
653 if (!duplicate) {
654 const ProStringList &outputs = project->values(ProKey(*it + ".variable_out"));
655 for(int output = 0; output < outputs.size(); ++output) {
656 if(outputs.at(output) != "OBJECT") {
657 isObj = false;
658 break;
659 }
660 }
661 sources.append(ProjectBuilderSources(inputs.at(input).toQString(), true,
662 (*it).toQString(), isObj));
663
664 if (isObj) {
665 inputs.removeAt(input);
666 continue;
667 }
668 }
669
670 ++input;
671 }
672 }
673 }
674 sources.append(ProjectBuilderSources("QMAKE_INTERNAL_INCLUDED_FILES"));
675 for(int source = 0; source < sources.size(); ++source) {
676 ProStringList &src_list = project->values(ProKey("QMAKE_PBX_" + sources.at(source).keyName()));
677 ProStringList &root_group_list = project->values("QMAKE_PBX_GROUPS");
678
679 const QStringList &files = fileFixify(sources.at(source).files(project),
680 FileFixifyFromOutdir | FileFixifyAbsolute);
681 for(int f = 0; f < files.count(); ++f) {
682 QString file = files[f];
683 if(!sources.at(source).compilerName().isNull() &&
684 !verifyExtraCompiler(sources.at(source).compilerName(), file))
685 continue;
686 if(file.endsWith(Option::prl_ext))
687 continue;
688 if (processedSources.contains(file))
689 continue;
690 processedSources.insert(file);
691
692 bool in_root = true;
693 QString src_key = keyFor(file);
694
695 QString name = file.split(Option::dir_sep).back();
696
697 if (!project->isActiveConfig("flat")) {
698 // Build group hierarchy for file references that match the source our build dir
699 QString relativePath = fileFixify(file, FileFixifyToIndir | FileFixifyRelative);
700 if (QDir::isRelativePath(relativePath) && relativePath.startsWith(QLatin1String("../")))
701 relativePath = fileFixify(file, FileFixifyRelative); // Try build dir
702
703 if (QDir::isRelativePath(relativePath)
704 && !relativePath.startsWith(QLatin1String("../"))
705 && relativePath.indexOf(Option::dir_sep) != -1) {
706 QString last_grp("QMAKE_PBX_" + sources.at(source).groupName() + "_HEIR_GROUP");
707 QStringList dirs = relativePath.split(Option::dir_sep);
708 name = dirs.back();
709 dirs.pop_back(); //remove the file portion as it will be added via src_key
710 for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
711 QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp));
712 if(dir_it == dirs.begin()) {
713 if(!src_list.contains(new_grp_key))
714 src_list.append(new_grp_key);
715 } else {
716 if(!groups[last_grp].contains(new_grp_key))
717 groups[last_grp] += new_grp_key;
718 }
719 last_grp = new_grp;
720 }
721 if (groups[last_grp].contains(src_key))
722 continue;
723 groups[last_grp] += src_key;
724 in_root = false;
725 }
726 }
727 if (in_root) {
728 if (src_list.contains(src_key))
729 continue;
730 src_list.append(src_key);
731 }
732 //source reference
733 t << "\t\t" << src_key << " = {\n"
734 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
735 << "\t\t\t" << writeSettings("path", file) << ";\n";
736 if (name != file)
737 t << "\t\t\t" << writeSettings("name", name) << ";\n";
738 t << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";\n";
739 QString filetype = xcodeFiletypeForFilename(file);
740 if (!filetype.isNull())
741 t << "\t\t\t" << writeSettings("lastKnownFileType", filetype) << ";\n";
742 t << "\t\t};\n";
743 if (sources.at(source).isBuildable()) { //build reference
744 QString build_key = keyFor(file + ".BUILDABLE");
745 t << "\t\t" << build_key << " = {\n"
746 << "\t\t\t" << writeSettings("fileRef", src_key) << ";\n"
747 << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";\n"
748 << "\t\t\tsettings = {\n"
749 << "\t\t\t\t" << writeSettings("ATTRIBUTES", ProStringList(), SettingsAsList, 5) << ";\n"
750 << "\t\t\t};\n"
751 << "\t\t};\n";
752 if (sources.at(source).isObjectOutput(file))
753 project->values("QMAKE_PBX_OBJ").append(build_key);
754 }
755 }
756 if(!src_list.isEmpty()) {
757 QString group_key = keyFor(sources.at(source).groupName());
758 if(root_group_list.indexOf(group_key) == -1)
759 root_group_list += group_key;
760
761 ProStringList &group = groups[sources.at(source).groupName()];
762 for(int src = 0; src < src_list.size(); ++src) {
763 if(group.indexOf(src_list.at(src)) == -1)
764 group += src_list.at(src);
765 }
766 }
767 }
768 for (QMap<QString, ProStringList>::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) {
769 t << "\t\t" << keyFor(grp_it.key()) << " = {\n"
770 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
771 << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";\n"
772 << "\t\t\t" << writeSettings("name", grp_it.key().section(Option::dir_sep, -1)) << ";\n"
773 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
774 << "\t\t};\n";
775 }
776
777 //PREPROCESS BUILDPHASE (just a makefile)
778 {
779 QString mkfile = pbx_dir + Option::dir_sep + "qt_preprocess.mak";
780 QFile mkf(mkfile);
781 ProStringList outputPaths;
782 ProStringList inputPaths;
783 if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
784 writingUnixMakefileGenerator = true;
785 debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
786 QTextStream mkt(&mkf);
787 writeHeader(mkt);
788 mkt << "MOC = " << var("QMAKE_MOC") << Qt::endl;
789 mkt << "UIC = " << var("QMAKE_UIC") << Qt::endl;
790 mkt << "LEX = " << var("QMAKE_LEX") << Qt::endl;
791 mkt << "LEXFLAGS = " << var("QMAKE_LEXFLAGS") << Qt::endl;
792 mkt << "YACC = " << var("QMAKE_YACC") << Qt::endl;
793 mkt << "YACCFLAGS = " << var("QMAKE_YACCFLAGS") << Qt::endl;
794 mkt << "DEFINES = "
795 << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
796 << varGlue("DEFINES","-D"," -D","") << Qt::endl;
797 mkt << "INCPATH =";
798 {
799 const ProStringList &incs = project->values("INCLUDEPATH");
800 for (ProStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit)
801 mkt << " -I" << escapeFilePath((*incit));
802 }
803 if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS"))
804 mkt << " " << var("QMAKE_FRAMEWORKPATH_FLAGS");
805 mkt << Qt::endl;
806 mkt << "DEL_FILE = " << var("QMAKE_DEL_FILE") << Qt::endl;
807 mkt << "MOVE = " << var("QMAKE_MOVE") << Qt::endl << Qt::endl;
808 mkt << "preprocess: compilers\n";
809 mkt << "clean preprocess_clean: compiler_clean\n\n";
810 writeExtraTargets(mkt);
811 if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
812 mkt << "compilers:";
813 const ProStringList &compilers = project->values("QMAKE_EXTRA_COMPILERS");
814 for(int compiler = 0; compiler < compilers.size(); ++compiler) {
815 const ProStringList &tmp_out = project->values(ProKey(compilers.at(compiler) + ".output"));
816 if (tmp_out.isEmpty())
817 continue;
818 const ProStringList &inputs = project->values(ProKey(compilers.at(compiler) + ".input"));
819 for(int input = 0; input < inputs.size(); ++input) {
820 const ProStringList &files = project->values(inputs.at(input).toKey());
821 if (files.isEmpty())
822 continue;
823 for(int file = 0, added = 0; file < files.size(); ++file) {
824 QString fn = files.at(file).toQString();
825 if (!verifyExtraCompiler(compilers.at(compiler), fn))
826 continue;
827 if(added && !(added % 3))
828 mkt << "\\\n\t";
829 ++added;
830 const QString file_name = fileFixify(fn, FileFixifyFromOutdir);
831 const QString tmpOut = fileFixify(tmp_out.first().toQString(), FileFixifyFromOutdir);
832 QString path = escapeDependencyPath(Option::fixPathToTargetOS(
833 replaceExtraCompilerVariables(tmpOut, file_name, QString(), NoShell)));
834 mkt << ' ' << path;
835 inputPaths << fn;
836 outputPaths << path;
837 }
838 }
839 }
840 mkt << Qt::endl;
841 writeExtraCompilerTargets(mkt);
842 writingUnixMakefileGenerator = false;
843 }
844 mkt.flush();
845 mkf.close();
846 }
847 // Remove duplicates from build steps with "combine"
848 outputPaths.removeDuplicates();
849
850 // Don't create cycles. We only have one qt_preprocess.mak which runs different compilers
851 // whose inputs may depend on the output of another. The "compilers" step will run all
852 // compilers anyway
853 inputPaths.removeEach(outputPaths);
854
855 mkfile = fileFixify(mkfile);
856 QString phase_key = keyFor("QMAKE_PBX_PREPROCESS_TARGET");
857 // project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
858 project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
859 t << "\t\t" << phase_key << " = {\n"
860 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
861 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
862 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
863 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
864 << "\t\t\t" << writeSettings("name", "Qt Preprocessors") << ";\n"
865 << "\t\t\t" << writeSettings("inputPaths", inputPaths, SettingsAsList, 4) << ";\n"
866 << "\t\t\t" << writeSettings("outputPaths", outputPaths, SettingsAsList, 4) << ";\n"
867 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";\n"
868 << "\t\t\t" << writeSettings("shellScript", "make -C " + IoUtils::shellQuoteUnix(Option::output_dir)
869 + " -f " + IoUtils::shellQuoteUnix(mkfile)) << ";\n"
870 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
871 << "\t\t};\n";
872 }
873
874 //SOURCE BUILDPHASE
875 if(!project->isEmpty("QMAKE_PBX_OBJ")) {
876 QString grp = "Compile Sources", key = keyFor(grp);
877 project->values("QMAKE_PBX_BUILDPHASES").append(key);
878 t << "\t\t" << key << " = {\n"
879 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
880 << "\t\t\t" << writeSettings("files", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";\n"
881 << "\t\t\t" << writeSettings("isa", "PBXSourcesBuildPhase", SettingsNoQuote) << ";\n"
882 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
883 << "\t\t\t" << writeSettings("name", grp) << ";\n"
884 << "\t\t};\n";
885 }
886
887 if(!project->isActiveConfig("staticlib")) { //DUMP LIBRARIES
888 const ProStringList defaultLibDirs = project->values("QMAKE_DEFAULT_LIBDIRS");
889 ProStringList &libdirs = project->values("QMAKE_PBX_LIBPATHS"),
890 &frameworkdirs = project->values("QMAKE_FRAMEWORKPATH");
891 static const char * const libs[] = { "LIBS", "LIBS_PRIVATE",
892 "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
893 for (int i = 0; libs[i]; i++) {
894 tmp = project->values(libs[i]);
895 for(int x = 0; x < tmp.count();) {
896 bool libSuffixReplaced = false;
897 bool remove = false;
898 QString library, name;
899 ProString opt = tmp[x];
900 if(opt.startsWith("-L")) {
901 QString r = opt.mid(2).toQString();
902 fixForOutput(r);
903 libdirs.append(r);
904 } else if(opt.startsWith("-l")) {
905 name = opt.mid(2).toQString();
906 QString lib("lib" + name);
907 for (ProStringList::Iterator lit = libdirs.begin(); lit != libdirs.end(); ++lit) {
908 if(project->isActiveConfig("link_prl")) {
909 const QString prlFilePath = QMakeMetaInfo::checkLib(
910 Option::normalizePath((*lit) + Option::dir_sep + lib
911 + Option::prl_ext));
912 if (replaceLibrarySuffix(prlFilePath, opt, name, library))
913 remove = true;
914 libSuffixReplaced = true;
915 }
916 if(!remove) {
917 QString extns[] = { ".dylib", ".so", ".a", QString() };
918 for(int n = 0; !remove && !extns[n].isNull(); n++) {
919 QString tmp = (*lit) + Option::dir_sep + lib + extns[n];
920 if(exists(tmp)) {
921 library = tmp;
922 debug_msg(1, "pbuilder: Found library (%s) via %s",
923 opt.toLatin1().constData(), library.toLatin1().constData());
924 remove = true;
925 }
926 }
927 }
928 }
929 } else if(opt.startsWith("-F")) {
930 QString r;
931 if(opt.size() > 2) {
932 r = opt.mid(2).toQString();
933 } else {
934 if(x == tmp.count()-1)
935 break;
936 r = tmp[++x].toQString();
937 }
938 if(!r.isEmpty()) {
939 fixForOutput(r);
940 frameworkdirs.append(r);
941 }
942 } else if(opt == "-framework") {
943 if(x == tmp.count()-1)
944 break;
945 const ProString &framework = tmp[x+1];
946 ProStringList fdirs = frameworkdirs;
947 fdirs << "/System/Library/Frameworks/" << "/Library/Frameworks/";
948 for(int fdir = 0; fdir < fdirs.count(); fdir++) {
949 if(exists(fdirs[fdir] + QDir::separator() + framework + ".framework")) {
950 remove = true;
951 library = fdirs[fdir] + Option::dir_sep + framework + ".framework";
952 tmp.removeAt(x);
953 break;
954 }
955 }
956 } else if (!opt.startsWith('-')) {
957 QString fn = opt.toQString();
958 if (exists(fn)) {
959 remove = true;
960 library = fn;
961 }
962 }
963 if(!library.isEmpty()) {
964 if (!libSuffixReplaced) {
965 const QFileInfo fi = fileInfo(library);
966 const QString prlFilePath = QMakeMetaInfo::checkLib(
967 Option::normalizePath(fi.absolutePath() + '/' + fi.completeBaseName()
968 + Option::prl_ext));
969 if (!prlFilePath.isEmpty()) {
970 name = fi.completeBaseName().mid(3);
971 replaceLibrarySuffix(prlFilePath, opt, name, library);
972 }
973 }
974 const int slsh = library.lastIndexOf(Option::dir_sep);
975 if(name.isEmpty()) {
976 if(slsh != -1)
977 name = library.right(library.length() - slsh - 1);
978 }
979 if(slsh != -1) {
980 const QString path = QFileInfo(library.left(slsh)).absoluteFilePath();
981 if (!path.isEmpty() && !libdirs.contains(path)
982 && !defaultLibDirs.contains(path)) {
983 libdirs += path;
984 }
985 }
986 library = fileFixify(library, FileFixifyFromOutdir | FileFixifyAbsolute);
987 QString key = keyFor(library);
988 if (!project->values("QMAKE_PBX_LIBRARIES").contains(key)) {
989 bool is_frmwrk = (library.endsWith(".framework"));
990 t << "\t\t" << key << " = {\n"
991 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
992 << "\t\t\t" << writeSettings("name", name) << ";\n"
993 << "\t\t\t" << writeSettings("path", library) << ";\n"
994 << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(library)), SettingsNoQuote) << ";\n"
995 << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";\n";
996 if (is_frmwrk)
997 t << "\t\t\t" << writeSettings("lastKnownFileType", "wrapper.framework") << ";\n";
998 t << "\t\t};\n";
999 project->values("QMAKE_PBX_LIBRARIES").append(key);
1000 QString build_key = keyFor(library + ".BUILDABLE");
1001 t << "\t\t" << build_key << " = {\n"
1002 << "\t\t\t" << writeSettings("fileRef", key) << ";\n"
1003 << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";\n"
1004 << "\t\t\tsettings = {\n"
1005 << "\t\t\t};\n"
1006 << "\t\t};\n";
1007 project->values("QMAKE_PBX_BUILD_LIBRARIES").append(build_key);
1008 }
1009 }
1010 if(remove)
1011 tmp.removeAt(x);
1012 else
1013 x++;
1014 }
1015 project->values(libs[i]) = tmp;
1016 }
1017 }
1018 //SUBLIBS BUILDPHASE (just another makefile)
1019 if(!project->isEmpty("SUBLIBS")) {
1020 QString mkfile = pbx_dir + Option::dir_sep + "qt_sublibs.mak";
1021 QFile mkf(mkfile);
1022 if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
1023 writingUnixMakefileGenerator = true;
1024 debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
1025 QTextStream mkt(&mkf);
1026 writeHeader(mkt);
1027 mkt << "SUBLIBS= ";
1028 // ### This is missing the parametrization found in unixmake2.cpp
1029 tmp = project->values("SUBLIBS");
1030 for(int i = 0; i < tmp.count(); i++)
1031 t << escapeFilePath("tmp/lib" + tmp[i] + ".a") << ' ';
1032 t << Qt::endl << Qt::endl;
1033 mkt << "sublibs: $(SUBLIBS)\n\n";
1034 tmp = project->values("SUBLIBS");
1035 for(int i = 0; i < tmp.count(); i++)
1036 t << escapeFilePath("tmp/lib" + tmp[i] + ".a") + ":\n\t"
1037 << var(ProKey("MAKELIB" + tmp[i])) << Qt::endl << Qt::endl;
1038 mkt.flush();
1039 mkf.close();
1040 writingUnixMakefileGenerator = false;
1041 }
1042 QString phase_key = keyFor("QMAKE_PBX_SUBLIBS_BUILDPHASE");
1043 mkfile = fileFixify(mkfile);
1044 project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key);
1045 t << "\t\t" << phase_key << " = {\n"
1046 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1047 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
1048 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
1049 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1050 << "\t\t\t" << writeSettings("name", "Qt Sublibs") << ";\n"
1051 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << "\n"
1052 << "\t\t\t" << writeSettings("shellScript", "make -C " + IoUtils::shellQuoteUnix(Option::output_dir)
1053 + " -f " + IoUtils::shellQuoteUnix(mkfile)) << ";\n"
1054 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
1055 << "\t\t};\n";
1056 }
1057
1058 if (!project->isEmpty("QMAKE_PRE_LINK")) {
1059 QString phase_key = keyFor("QMAKE_PBX_PRELINK_BUILDPHASE");
1060 project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
1061
1062 ProStringList inputPaths;
1063 ProStringList outputPaths;
1064 const ProStringList &archs = project->values("QMAKE_XCODE_ARCHS");
1065 if (!archs.isEmpty()) {
1066 const int size = archs.size();
1067 inputPaths.reserve(size);
1068 outputPaths.reserve(size);
1069 for (int i = 0; i < size; ++i) {
1070 const ProString &arch = archs.at(i);
1071 inputPaths << "$(OBJECT_FILE_DIR_$(CURRENT_VARIANT))/" + arch + "/";
1072 outputPaths << "$(LINK_FILE_LIST_$(CURRENT_VARIANT)_" + arch + ")";
1073 }
1074 } else {
1075 inputPaths << "$(OBJECT_FILE_DIR_$(CURRENT_VARIANT))/$(CURRENT_ARCH)/";
1076 outputPaths << "$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(CURRENT_ARCH))";
1077 }
1078
1079 t << "\t\t" << phase_key << " = {\n"
1080 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1081 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
1082 // The build phases are not executed in the order they are defined, but by their
1083 // resolved dependenices, so we have to ensure that this phase is run after the
1084 // compilation phase, and before the link phase. Making the phase depend on the
1085 // object file directory, and "write" to the list of files to link achieves that.
1086 << "\t\t\t" << writeSettings("inputPaths", inputPaths, SettingsAsList, 4) << ";\n"
1087 << "\t\t\t" << writeSettings("outputPaths", outputPaths, SettingsAsList, 4) << ";\n"
1088 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
1089 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1090 << "\t\t\t" << writeSettings("name", "Qt Prelink") << ";\n"
1091 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";\n"
1092 << "\t\t\t" << writeSettings("shellScript", project->values("QMAKE_PRE_LINK")) << ";\n"
1093 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
1094 << "\t\t};\n";
1095 }
1096
1097 //LIBRARY BUILDPHASE
1098 if(!project->isEmpty("QMAKE_PBX_LIBRARIES")) {
1099 tmp = project->values("QMAKE_PBX_LIBRARIES");
1100 if(!tmp.isEmpty()) {
1101 QString grp("Frameworks"), key = keyFor(grp);
1102 project->values("QMAKE_PBX_GROUPS").append(key);
1103 t << "\t\t" << key << " = {\n"
1104 << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_LIBRARIES"), SettingsAsList, 4) << ";\n"
1105 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
1106 << "\t\t\t" << writeSettings("name", grp) << ";\n"
1107 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
1108 << "\t\t};\n";
1109 }
1110 }
1111 {
1112 QString grp("Link Binary With Libraries"), key = keyFor(grp);
1113 project->values("QMAKE_PBX_BUILDPHASES").append(key);
1114 t << "\t\t" << key << " = {\n"
1115 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1116 << "\t\t\t" << writeSettings("files", project->values("QMAKE_PBX_BUILD_LIBRARIES"), SettingsAsList, 4) << ";\n"
1117 << "\t\t\t" << writeSettings("isa", "PBXFrameworksBuildPhase", SettingsNoQuote) << ";\n"
1118 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1119 << "\t\t\t" << writeSettings("name", grp) << ";\n"
1120 << "\t\t};\n";
1121 }
1122
1123 if (!project->isEmpty("QMAKE_POST_LINK")) {
1124 QString phase_key = keyFor("QMAKE_PBX_POSTLINK_BUILDPHASE");
1125 project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
1126 t << "\t\t" << phase_key << " = {\n"
1127 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1128 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
1129 // The build phases are not executed in the order they are defined, but by their
1130 // resolved dependenices, so we have to ensure the phase is run after linking.
1131 << "\t\t\t" << writeSettings("inputPaths", ProStringList("$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)"), SettingsAsList, 4) << ";\n"
1132 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
1133 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1134 << "\t\t\t" << writeSettings("name", "Qt Postlink") << ";\n"
1135 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";\n"
1136 << "\t\t\t" << writeSettings("shellScript", project->values("QMAKE_POST_LINK")) << ";\n"
1137 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
1138 << "\t\t};\n";
1139 }
1140
1141 if (!project->isEmpty("DESTDIR")) {
1142 QString phase_key = keyFor("QMAKE_PBX_TARGET_COPY_PHASE");
1143 QString destDir = fileFixify(project->first("DESTDIR").toQString(),
1144 FileFixifyFromOutdir | FileFixifyAbsolute);
1145 project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
1146 t << "\t\t" << phase_key << " = {\n"
1147 << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";\n"
1148 << "\t\t\t" << writeSettings("name", "Project Copy") << ";\n"
1149 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1150 << "\t\t\t" << writeSettings("files", ProStringList(), SettingsAsList, 4) << ";\n"
1151 << "\t\t\t" << writeSettings("inputPaths", ProStringList(), SettingsAsList, 4) << ";\n"
1152 << "\t\t\t" << writeSettings("outputPaths", ProStringList(), SettingsAsList, 4) << ";\n"
1153 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1154 << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";\n"
1155 << "\t\t\t" << writeSettings("shellScript", fixForOutput("cp -r $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME " + IoUtils::shellQuoteUnix(destDir))) << ";\n"
1156 << "\t\t\t" << writeSettings("showEnvVarsInLog", "0") << ";\n"
1157 << "\t\t};\n";
1158 }
1159 bool copyBundleResources = project->isActiveConfig("app_bundle") && project->first("TEMPLATE") == "app";
1160 ProStringList bundle_resources_files;
1161 ProStringList embedded_frameworks;
1162 QMap<ProString, ProStringList> embedded_plugins;
1163 // Copy Bundle Data
1164 if (!project->isEmpty("QMAKE_BUNDLE_DATA")) {
1165 ProStringList bundle_file_refs;
1166 bool osx = project->isActiveConfig("osx");
1167
1168 //all bundle data
1169 const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA");
1170 for(int i = 0; i < bundle_data.count(); i++) {
1171 ProStringList bundle_files;
1172 ProString path = project->first(ProKey(bundle_data[i] + ".path"));
1173 const bool isEmbeddedFramework = ((!osx && path == QLatin1String("Frameworks"))
1174 || (osx && path == QLatin1String("Contents/Frameworks")));
1175 const ProString pluginsPrefix = ProString(osx ? QLatin1String("Contents/PlugIns") : QLatin1String("PlugIns"));
1176 const bool isEmbeddedPlugin = (path == pluginsPrefix) || path.startsWith(pluginsPrefix + "/");
1177
1178 //all files
1179 const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files"));
1180 for(int file = 0; file < files.count(); file++) {
1181 QString fn = fileFixify(files[file].toQString(), FileFixifyAbsolute);
1182 QString name = fn.split(Option::dir_sep).back();
1183 QString file_ref_key = keyFor("QMAKE_PBX_BUNDLE_DATA_FILE_REF." + bundle_data[i] + "-" + fn);
1184 bundle_file_refs += file_ref_key;
1185 t << "\t\t" << file_ref_key << " = {\n"
1186 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
1187 << "\t\t\t" << writeSettings("path", fn) << ";\n"
1188 << "\t\t\t" << writeSettings("name", name) << ";\n"
1189 << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";\n"
1190 << "\t\t};\n";
1191 QString file_key = keyFor("QMAKE_PBX_BUNDLE_DATA_FILE." + bundle_data[i] + "-" + fn);
1192 bundle_files += file_key;
1193 t << "\t\t" << file_key << " = {\n"
1194 << "\t\t\t" << writeSettings("fileRef", file_ref_key) << ";\n"
1195 << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";\n";
1196 if (isEmbeddedFramework || isEmbeddedPlugin || name.endsWith(".dylib") || name.endsWith(".framework"))
1197 t << "\t\t\t" << writeSettings("settings", "{ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }", SettingsNoQuote) << ";\n";
1198 t << "\t\t};\n";
1199 }
1200
1201 if (copyBundleResources && ((!osx && path.isEmpty())
1202 || (osx && path == QLatin1String("Contents/Resources")))) {
1203 for (const ProString &s : qAsConst(bundle_files))
1204 bundle_resources_files << s;
1205 } else if (copyBundleResources && isEmbeddedFramework) {
1206 for (const ProString &s : qAsConst(bundle_files))
1207 embedded_frameworks << s;
1208 } else if (copyBundleResources && isEmbeddedPlugin) {
1209 for (const ProString &s : qAsConst(bundle_files)) {
1210 ProString subpath = (path == pluginsPrefix) ? ProString() : path.mid(pluginsPrefix.size() + 1);
1211 embedded_plugins[subpath] << s;
1212 }
1213 } else {
1214 QString phase_key = keyFor("QMAKE_PBX_BUNDLE_COPY." + bundle_data[i]);
1215 //if (!project->isActiveConfig("shallow_bundle")
1216 // && !project->isEmpty(ProKey(bundle_data[i] + ".version"))) {
1217 //}
1218
1219 project->values("QMAKE_PBX_BUILDPHASES").append(phase_key);
1220 t << "\t\t" << phase_key << " = {\n"
1221 << "\t\t\t" << writeSettings("name", "Copy '" + bundle_data[i] + "' Files to Bundle") << ";\n"
1222 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1223 << "\t\t\t" << writeSettings("dstPath", path) << ";\n"
1224 << "\t\t\t" << writeSettings("dstSubfolderSpec", "1", SettingsNoQuote) << ";\n"
1225 << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";\n"
1226 << "\t\t\t" << writeSettings("files", bundle_files, SettingsAsList, 4) << ";\n"
1227 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1228 << "\t\t};\n";
1229 }
1230 }
1231
1232 QString bundle_data_key = keyFor("QMAKE_PBX_BUNDLE_DATA");
1233 project->values("QMAKE_PBX_GROUPS").append(bundle_data_key);
1234 t << "\t\t" << bundle_data_key << " = {\n"
1235 << "\t\t\t" << writeSettings("children", bundle_file_refs, SettingsAsList, 4) << ";\n"
1236 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
1237 << "\t\t\t" << writeSettings("name", "Bundle Data") << ";\n"
1238 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
1239 << "\t\t};\n";
1240 }
1241
1242 // Copy bundle resources. Optimizes resources, and puts them into the Resources
1243 // subdirectory on OSX, but doesn't support paths.
1244 if (copyBundleResources) {
1245 if (!project->isEmpty("ICON")) {
1246 ProString icon = project->first("ICON");
1247 bundle_resources_files += keyFor(icon + ".BUILDABLE");
1248 }
1249
1250 // Always add "Copy Bundle Resources" phase, even when we have no bundle
1251 // resources, since Xcode depends on it being there for e.g asset catalogs.
1252 QString grp("Copy Bundle Resources"), key = keyFor(grp);
1253 project->values("QMAKE_PBX_BUILDPHASES").append(key);
1254 t << "\t\t" << key << " = {\n"
1255 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1256 << "\t\t\t" << writeSettings("files", bundle_resources_files, SettingsAsList, 4) << ";\n"
1257 << "\t\t\t" << writeSettings("isa", "PBXResourcesBuildPhase", SettingsNoQuote) << ";\n"
1258 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1259 << "\t\t\t" << writeSettings("name", grp) << ";\n"
1260 << "\t\t};\n";
1261
1262 QString grp2("Embed Frameworks"), key2 = keyFor(grp2);
1263 project->values("QMAKE_PBX_BUILDPHASES").append(key2);
1264 t << "\t\t" << key2 << " = {\n"
1265 << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";\n"
1266 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1267 << "\t\t\t" << writeSettings("dstPath", "") << ";\n"
1268 << "\t\t\t" << writeSettings("dstSubfolderSpec", "10", SettingsNoQuote) << ";\n"
1269 << "\t\t\t" << writeSettings("files", embedded_frameworks, SettingsAsList, 4) << ";\n"
1270 << "\t\t\t" << writeSettings("name", grp2) << ";\n"
1271 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1272 << "\t\t};\n";
1273
1274 for (auto it = embedded_plugins.cbegin(), end = embedded_plugins.cend(); it != end; ++it) {
1275 QString suffix = !it.key().isEmpty() ? (" (" + it.key() + ")") : QString();
1276 QString grp3("Embed PlugIns" + suffix), key3 = keyFor(grp3);
1277 project->values("QMAKE_PBX_BUILDPHASES").append(key3);
1278 t << "\t\t" << key3 << " = {\n"
1279 << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";\n"
1280 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1281 << "\t\t\t" << writeSettings("dstPath", it.key()) << ";\n"
1282 << "\t\t\t" << writeSettings("dstSubfolderSpec", "13", SettingsNoQuote) << ";\n"
1283 << "\t\t\t" << writeSettings("files", it.value(), SettingsAsList, 4) << ";\n"
1284 << "\t\t\t" << writeSettings("name", grp3) << ";\n"
1285 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1286 << "\t\t};\n";
1287 }
1288 }
1289
1290 //REFERENCE
1291 project->values("QMAKE_PBX_PRODUCTS").append(keyFor(pbx_dir + "QMAKE_PBX_REFERENCE"));
1292 t << "\t\t" << keyFor(pbx_dir + "QMAKE_PBX_REFERENCE") << " = {\n"
1293 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
1294 << "\t\t\t" << writeSettings("includeInIndex", "0", SettingsNoQuote) << ";\n";
1295 if(project->first("TEMPLATE") == "app") {
1296 ProString targ = project->first("QMAKE_ORIG_TARGET");
1297 if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
1298 if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
1299 targ = project->first("QMAKE_BUNDLE_NAME");
1300 targ += project->first("QMAKE_BUNDLE_EXTENSION");
1301 if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE"))
1302 t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) + ";\n";
1303 } else if(project->isActiveConfig("app_bundle")) {
1304 if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
1305 targ = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
1306 targ += ".app";
1307 t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.application") << ";\n";
1308 } else {
1309 t << "\t\t\t" << writeSettings("explicitFileType", "compiled.mach-o.executable") << ";\n";
1310 }
1311 t << "\t\t\t" << writeSettings("path", targ) << ";\n";
1312 } else {
1313 ProString lib = project->first("QMAKE_ORIG_TARGET");
1314 if(project->isActiveConfig("staticlib")) {
1315 lib = project->first("TARGET");
1316 } else if(!project->isActiveConfig("lib_bundle")) {
1317 if(project->isActiveConfig("plugin"))
1318 lib = project->first("TARGET");
1319 else
1320 lib = project->first("TARGET_");
1321 }
1322 int slsh = lib.lastIndexOf(Option::dir_sep);
1323 if(slsh != -1)
1324 lib = lib.right(lib.length() - slsh - 1);
1325 if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
1326 if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
1327 lib = project->first("QMAKE_BUNDLE_NAME");
1328 lib += project->first("QMAKE_BUNDLE_EXTENSION");
1329 if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE"))
1330 t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) << ";\n";
1331 } else if(!project->isActiveConfig("staticlib") && project->isActiveConfig("lib_bundle")) {
1332 if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
1333 lib = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
1334 lib += ".framework";
1335 t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.framework") << ";\n";
1336 } else {
1337 t << "\t\t\t" << writeSettings("explicitFileType", "compiled.mach-o.dylib") << ";\n";
1338 }
1339 t << "\t\t\t" << writeSettings("path", lib) << ";\n";
1340 }
1341 t << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";\n"
1342 << "\t\t};\n";
1343 { //Products group
1344 QString grp("Products"), key = keyFor(grp);
1345 project->values("QMAKE_PBX_GROUPS").append(key);
1346 t << "\t\t" << key << " = {\n"
1347 << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_PRODUCTS"), SettingsAsList, 4) << ";\n"
1348 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
1349 << "\t\t\t" << writeSettings("name", "Products") << ";\n"
1350 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
1351 << "\t\t};\n";
1352 }
1353
1354 //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER
1355 //ROOT_GROUP
1356 t << "\t\t" << keyFor("QMAKE_PBX_ROOT_GROUP") << " = {\n"
1357 << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_GROUPS"), SettingsAsList, 4) << ";\n"
1358 << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";\n"
1359 << "\t\t\t" << writeSettings("name", project->first("QMAKE_ORIG_TARGET")) << ";\n"
1360 << "\t\t\t" << writeSettings("sourceTree", "<group>") << ";\n"
1361 << "\t\t};\n";
1362
1363 {
1364 QString aggregate_target_key = keyFor(pbx_dir + "QMAKE_PBX_AGGREGATE_TARGET");
1365 project->values("QMAKE_PBX_TARGETS").append(aggregate_target_key);
1366 t << "\t\t" << aggregate_target_key << " = {\n"
1367 << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES"), SettingsAsList, 4) << ";\n"
1368 << "\t\t\t" << writeSettings("dependencies", ProStringList(), SettingsAsList, 4) << ";\n"
1369 << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST_TARGET"), SettingsNoQuote) << ";\n"
1370 << "\t\t\t" << writeSettings("isa", "PBXAggregateTarget", SettingsNoQuote) << ";\n"
1371 << "\t\t\t" << writeSettings("buildRules", ProStringList(), SettingsAsList) << ";\n"
1372 << "\t\t\t" << writeSettings("productName", "Qt Preprocess") << ";\n"
1373 << "\t\t\t" << writeSettings("name", "Qt Preprocess") << ";\n"
1374 << "\t\t};\n";
1375
1376 QString aggregate_target_dep_key = keyFor(pbx_dir + "QMAKE_PBX_AGGREGATE_TARGET_DEP");
1377 t << "\t\t" << aggregate_target_dep_key << " = {\n"
1378 << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";\n"
1379 << "\t\t\t" << writeSettings("target", aggregate_target_key) << ";\n"
1380 << "\t\t};\n";
1381
1382 project->values("QMAKE_PBX_TARGET_DEPENDS").append(aggregate_target_dep_key);
1383 }
1384
1385 //TARGET
1386 QString target_key = keyFor(pbx_dir + "QMAKE_PBX_TARGET");
1387 project->values("QMAKE_PBX_TARGETS").prepend(target_key);
1388 t << "\t\t" << target_key << " = {\n"
1389 << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_BUILDPHASES"),
1390 SettingsAsList, 4) << ";\n";
1391 t << "\t\t\t" << writeSettings("dependencies", project->values("QMAKE_PBX_TARGET_DEPENDS"), SettingsAsList, 4) << ";\n"
1392 << "\t\t\t" << writeSettings("productReference", keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")) << ";\n";
1393 t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST_TARGET"), SettingsNoQuote) << ";\n";
1394 t << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";\n";
1395 t << "\t\t\t" << writeSettings("buildRules", ProStringList(), SettingsAsList) << ";\n";
1396 if(project->first("TEMPLATE") == "app") {
1397 if (!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE")) {
1398 t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";\n";
1399 } else {
1400 if (project->isActiveConfig("app_bundle"))
1401 t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.application") << ";\n";
1402 else
1403 t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.tool") << ";\n";
1404 }
1405 t << "\t\t\t" << writeSettings("name", project->first("QMAKE_ORIG_TARGET")) << ";\n"
1406 << "\t\t\t" << writeSettings("productName", project->first("QMAKE_ORIG_TARGET")) << ";\n";
1407 } else {
1408 ProString lib = project->first("QMAKE_ORIG_TARGET");
1409 if(!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib"))
1410 lib.prepend("lib");
1411 t << "\t\t\t" << writeSettings("name", lib) << ";\n"
1412 << "\t\t\t" << writeSettings("productName", lib) << ";\n";
1413 if (!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE"))
1414 t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";\n";
1415 else if (project->isActiveConfig("staticlib"))
1416 t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.static") << ";\n";
1417 else if (project->isActiveConfig("lib_bundle"))
1418 t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.framework") << ";\n";
1419 else
1420 t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.dynamic") << ";\n";
1421 }
1422 if(!project->isEmpty("DESTDIR"))
1423 t << "\t\t\t" << writeSettings("productInstallPath", project->first("DESTDIR")) << ";\n";
1424 t << "\t\t};\n";
1425
1426 // Test target for running Qt unit tests under XCTest
1427 if (project->isActiveConfig("testcase") && project->isActiveConfig("app_bundle")) {
1428 QString devNullFileReferenceKey = keyFor(pbx_dir + "QMAKE_PBX_DEV_NULL_FILE_REFERENCE");
1429 t << "\t\t" << devNullFileReferenceKey << " = {\n"
1430 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
1431 << "\t\t\t" << writeSettings("name", "/dev/null") << ";\n"
1432 << "\t\t\t" << writeSettings("path", "/dev/null") << ";\n"
1433 << "\t\t\t" << writeSettings("lastKnownFileType", "sourcecode.c.c") << ";\n"
1434 << "\t\t\t" << writeSettings("sourceTree", "<absolute>") << ";\n"
1435 << "\t\t};\n";
1436
1437 QString devNullBuildFileKey = keyFor(pbx_dir + "QMAKE_PBX_DEV_NULL_BUILD_FILE");
1438 t << "\t\t" << devNullBuildFileKey << " = {\n"
1439 << "\t\t\t" << writeSettings("fileRef", devNullFileReferenceKey) << ";\n"
1440 << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";\n"
1441 << "\t\t};\n";
1442
1443 QString dummySourceBuildPhaseKey = keyFor(pbx_dir + "QMAKE_PBX_DUMMY_SOURCE_BUILD_PHASE");
1444 t << "\t\t" << dummySourceBuildPhaseKey << " = {\n"
1445 << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";\n"
1446 << "\t\t\t" << writeSettings("files", devNullBuildFileKey, SettingsAsList, 4) << ";\n"
1447 << "\t\t\t" << writeSettings("isa", "PBXSourcesBuildPhase", SettingsNoQuote) << ";\n"
1448 << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";\n"
1449 << "\t\t};\n";
1450
1451 ProStringList testBundleBuildConfigs;
1452
1453 ProString targetName = project->first("QMAKE_ORIG_TARGET");
1454 ProString testHost = "$(BUILT_PRODUCTS_DIR)/" + targetName + ".app/";
1455 if (project->isActiveConfig("osx"))
1456 testHost.append("Contents/MacOS/");
1457 testHost.append(targetName);
1458
1459 static const char * const configs[] = { "Debug", "Release", nullptr };
1460 for (int i = 0; configs[i]; i++) {
1461 QString testBundleBuildConfig = keyFor(pbx_dir + "QMAKE_PBX_TEST_BUNDLE_BUILDCONFIG_" + configs[i]);
1462 t << "\t\t" << testBundleBuildConfig << " = {\n"
1463 << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";\n"
1464 << "\t\t\tbuildSettings = {\n"
1465 << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", project->first("QMAKE_XCODE_SPECDIR") + "/QtTest.plist") << ";\n"
1466 << "\t\t\t\t" << writeSettings("OTHER_LDFLAGS", "") << ";\n"
1467 << "\t\t\t\t" << writeSettings("TEST_HOST", testHost) << ";\n"
1468 << "\t\t\t\t" << writeSettings("DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym") << ";\n"
1469 << "\t\t\t};\n"
1470 << "\t\t\t" << writeSettings("name", configs[i], SettingsNoQuote) << ";\n"
1471 << "\t\t};\n";
1472
1473 testBundleBuildConfigs.append(testBundleBuildConfig);
1474 }
1475
1476 QString testBundleBuildConfigurationListKey = keyFor(pbx_dir + "QMAKE_PBX_TEST_BUNDLE_BUILDCONFIG_LIST");
1477 t << "\t\t" << testBundleBuildConfigurationListKey << " = {\n"
1478 << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";\n"
1479 << "\t\t\t" << writeSettings("buildConfigurations", testBundleBuildConfigs, SettingsAsList, 4) << ";\n"
1480 << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";\n"
1481 << "\t\t\t" << writeSettings("defaultConfigurationName", "Debug", SettingsNoQuote) << ";\n"
1482 << "\t\t};\n";
1483
1484 QString primaryTargetDependencyKey = keyFor(pbx_dir + "QMAKE_PBX_PRIMARY_TARGET_DEP");
1485 t << "\t\t" << primaryTargetDependencyKey << " = {\n"
1486 << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";\n"
1487 << "\t\t\t" << writeSettings("target", keyFor(pbx_dir + "QMAKE_PBX_TARGET")) << ";\n"
1488 << "\t\t};\n";
1489
1490 QString testBundleReferenceKey = keyFor("QMAKE_TEST_BUNDLE_REFERENCE");
1491 t << "\t\t" << testBundleReferenceKey << " = {\n"
1492 << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";\n"
1493 << "\t\t\t" << writeSettings("explicitFileType", "wrapper.cfbundle") << ";\n"
1494 << "\t\t\t" << writeSettings("includeInIndex", "0", SettingsNoQuote) << ";\n"
1495 << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";\n"
1496 << "\t\t};\n";
1497
1498 QString testTargetKey = keyFor(pbx_dir + "QMAKE_PBX_TEST_TARGET");
1499 project->values("QMAKE_PBX_TARGETS").append(testTargetKey);
1500 t << "\t\t" << testTargetKey << " = {\n"
1501 << "\t\t\t" << writeSettings("buildPhases", dummySourceBuildPhaseKey, SettingsAsList, 4) << ";\n"
1502 << "\t\t\t" << writeSettings("dependencies", primaryTargetDependencyKey, SettingsAsList, 4) << ";\n"
1503 << "\t\t\t" << writeSettings("buildConfigurationList", testBundleBuildConfigurationListKey) << ";\n"
1504 << "\t\t\t" << writeSettings("productType", "com.apple.product-type.bundle.unit-test") << ";\n"
1505 << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";\n"
1506 << "\t\t\t" << writeSettings("productReference", testBundleReferenceKey) << ";\n"
1507 << "\t\t\t" << writeSettings("name", "Qt Test") << ";\n"
1508 << "\t\t};\n";
1509
1510 QLatin1String testTargetID("TestTargetID");
1511 project->values(ProKey("QMAKE_PBX_TARGET_ATTRIBUTES_" + testTargetKey + "_" + testTargetID)).append(keyFor(pbx_dir + "QMAKE_PBX_TARGET"));
1512 project->values(ProKey("QMAKE_PBX_TARGET_ATTRIBUTES_" + testTargetKey)).append(ProKey(testTargetID));
1513 }
1514
1515 //DEBUG/RELEASE
1516 QString defaultConfig;
1517 for(int as_release = 0; as_release < 2; as_release++)
1518 {
1519 QString configName = (as_release ? "Release" : "Debug");
1520
1521 QMap<QString, QString> settings;
1522 if (!project->isActiveConfig("no_xcode_development_team")) {
1523 QString teamId;
1524 if (!project->isEmpty("QMAKE_DEVELOPMENT_TEAM")) {
1525 teamId = project->first("QMAKE_DEVELOPMENT_TEAM").toQString();
1526 } else {
1527 const QList<QVariantMap> teams = provisioningTeams();
1528 if (!teams.isEmpty()) // first suitable team we find is the one we'll use by default
1529 teamId = teams.first().value(QLatin1String("teamID")).toString();
1530 }
1531 if (!teamId.isEmpty())
1532 settings.insert("DEVELOPMENT_TEAM", teamId);
1533 if (!project->isEmpty("QMAKE_PROVISIONING_PROFILE"))
1534 settings.insert("PROVISIONING_PROFILE_SPECIFIER", project->first("QMAKE_PROVISIONING_PROFILE").toQString());
1535 }
1536
1537 settings.insert("APPLICATION_EXTENSION_API_ONLY", project->isActiveConfig("app_extension_api_only") ? "YES" : "NO");
1538 // required for tvOS (and watchos), optional on iOS (deployment target >= iOS 6.0)
1539 settings.insert("ENABLE_BITCODE", project->isActiveConfig("bitcode") ? "YES" : "NO");
1540 if(!as_release)
1541 settings.insert("GCC_OPTIMIZATION_LEVEL", "0");
1542 if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK"))
1543 settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK").toQString());
1544 {
1545 const ProStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS");
1546 for(int i = 0; i < l.size(); ++i) {
1547 ProString name = l.at(i);
1548 const ProKey buildKey(name + ".build");
1549 if (!project->isEmpty(buildKey)) {
1550 const QString build = project->first(buildKey).toQString();
1551 if (build.toLower() != configName.toLower())
1552 continue;
1553 }
1554 const QString value = project->values(ProKey(name + ".value")).join(QString(Option::field_sep));
1555 const ProKey nkey(name + ".name");
1556 if (!project->isEmpty(nkey))
1557 name = project->first(nkey);
1558 settings.insert(name.toQString(), value);
1559 }
1560 }
1561 if (project->first("TEMPLATE") == "app") {
1562 settings.insert("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET").toQString()));
1563 } else {
1564 ProString lib = project->first("QMAKE_ORIG_TARGET");
1565 if (!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib"))
1566 lib.prepend("lib");
1567 settings.insert("PRODUCT_NAME", lib.toQString());
1568 }
1569
1570 if (project->isActiveConfig("debug") != (bool)as_release)
1571 defaultConfig = configName;
1572 for (int i = 0; i < buildConfigGroups.size(); i++) {
1573 QString key = keyFor("QMAKE_PBX_BUILDCONFIG_" + configName + buildConfigGroups.at(i));
1574 project->values(ProKey("QMAKE_PBX_BUILDCONFIGS_" + buildConfigGroups.at(i))).append(key);
1575 t << "\t\t" << key << " = {\n"
1576 << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";\n"
1577 << "\t\t\tbuildSettings = {\n";
1578 for (QMap<QString, QString>::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it)
1579 t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n";
1580 if (buildConfigGroups.at(i) == QLatin1String("PROJECT")) {
1581 if (!project->isEmpty("QMAKE_XCODE_GCC_VERSION"))
1582 t << "\t\t\t\t" << writeSettings("GCC_VERSION", project->first("QMAKE_XCODE_GCC_VERSION"), SettingsNoQuote) << ";\n";
1583 ProString program = project->first("QMAKE_CC");
1584 if (!program.isEmpty())
1585 t << "\t\t\t\t" << writeSettings("CC", fixForOutput(findProgram(program))) << ";\n";
1586 program = project->first("QMAKE_CXX");
1587 // Xcode will automatically take care of using CC with the right -x option,
1588 // and will actually break if we pass CPLUSPLUS, by adding an additional set of "++"
1589 if (!program.isEmpty() && !program.contains("clang++"))
1590 t << "\t\t\t\t" << writeSettings("CPLUSPLUS", fixForOutput(findProgram(program))) << ";\n";
1591 program = project->first("QMAKE_LINK");
1592 if (!program.isEmpty())
1593 t << "\t\t\t\t" << writeSettings("LDPLUSPLUS", fixForOutput(findProgram(program))) << ";\n";
1594
1595 if ((project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) ||
1596 (project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
1597 project->isActiveConfig("lib_bundle"))) {
1598 QString plist = fileFixify(project->first("QMAKE_INFO_PLIST").toQString(), FileFixifyToIndir);
1599 if (!plist.isEmpty()) {
1600 if (exists(plist))
1601 t << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", fileFixify(plist)) << ";\n";
1602 else
1603 warn_msg(WarnLogic, "Could not resolve Info.plist: '%s'. Check if QMAKE_INFO_PLIST points to a valid file.", plist.toLatin1().constData());
1604 } else {
1605 plist = fileFixify(specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"), FileFixifyBackwards);
1606 QFile plist_in_file(plist);
1607 if (plist_in_file.open(QIODevice::ReadOnly)) {
1608 QTextStream plist_in(&plist_in_file);
1609 QString plist_in_text = plist_in.readAll();
1610 plist_in_text.replace(QLatin1String("@ICON@"),
1611 (project->isEmpty("ICON") ? QString("") : project->first("ICON").toQString().section(Option::dir_sep, -1)));
1612 if (project->first("TEMPLATE") == "app") {
1613 ProString app_bundle_name = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
1614 if (app_bundle_name.isEmpty())
1615 app_bundle_name = project->first("QMAKE_ORIG_TARGET");
1616 plist_in_text.replace(QLatin1String("@EXECUTABLE@"), app_bundle_name.toQString());
1617 } else {
1618 ProString lib_bundle_name = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
1619 if (lib_bundle_name.isEmpty())
1620 lib_bundle_name = project->first("QMAKE_ORIG_TARGET");
1621 plist_in_text.replace(QLatin1String("@LIBRARY@"), lib_bundle_name.toQString());
1622 }
1623 QString bundlePrefix = project->first("QMAKE_TARGET_BUNDLE_PREFIX").toQString();
1624 if (bundlePrefix.isEmpty())
1625 bundlePrefix = "com.yourcompany";
1626 plist_in_text.replace(QLatin1String("@BUNDLEIDENTIFIER@"), bundlePrefix + '.' + QLatin1String("${PRODUCT_NAME:rfc1034identifier}"));
1627 if (!project->values("VERSION").isEmpty()) {
1628 plist_in_text.replace(QLatin1String("@SHORT_VERSION@"), project->first("VER_MAJ") + "." + project->first("VER_MIN"));
1629 }
1630 plist_in_text.replace(QLatin1String("@TYPEINFO@"),
1631 (project->isEmpty("QMAKE_PKGINFO_TYPEINFO")
1632 ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4).toQString()));
1633 QFile plist_out_file(Option::output_dir + "/Info.plist");
1634 if (plist_out_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
1635 QTextStream plist_out(&plist_out_file);
1636 plist_out << plist_in_text;
1637 t << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", "Info.plist") << ";\n";
1638 } else {
1639 warn_msg(WarnLogic, "Could not write Info.plist: '%s'.", plist_out_file.fileName().toLatin1().constData());
1640 }
1641 } else {
1642 warn_msg(WarnLogic, "Could not open Info.plist: '%s'.", plist.toLatin1().constData());
1643 }
1644 }
1645 }
1646
1647 if (Option::output_dir != qmake_getpwd()) {
1648 // The SYMROOT is marked by Xcode as excluded from Time Machine
1649 // backups, as it's a temporary build dir, but that's fine when
1650 // we are shadow building.
1651 t << "\t\t\t\t" << writeSettings("SYMROOT", "$(PROJECT_DIR)") << ";\n";
1652 } else {
1653 // For in-source builds we don't want to exclude the sources
1654 // from being backed up, so we point SYMROOT to a temporary
1655 // build directory.
1656 t << "\t\t\t\t" << writeSettings("SYMROOT", ".xcode") << ";\n";
1657
1658 // Then we set the configuration build dir instead, so that the
1659 // final build artifacts end up in the place Qt Creator expects.
1660 // The disadvantage of using this over SYMROOT is that Xcode will
1661 // fail to archive projects that override this variable.
1662 t << "\t\t\t\t" << writeSettings("CONFIGURATION_BUILD_DIR",
1663 "$(PROJECT_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)") << ";\n";
1664 }
1665
1666 if (!project->isEmpty("DESTDIR")) {
1667 ProString dir = project->first("DESTDIR");
1668 if (QDir::isRelativePath(dir.toQString()))
1669 dir.prepend(Option::output_dir + Option::dir_sep);
1670 t << "\t\t\t\t" << writeSettings("INSTALL_DIR", dir) << ";\n";
1671 }
1672
1673 if (project->first("TEMPLATE") == "lib")
1674 t << "\t\t\t\t" << writeSettings("INSTALL_PATH", ProStringList()) << ";\n";
1675
1676 if (!project->isEmpty("VERSION") && project->first("VERSION") != "0.0.0") {
1677 t << "\t\t\t\t" << writeSettings("DYLIB_CURRENT_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")+"."+project->first("VER_PAT")) << ";\n";
1678 if (project->isEmpty("COMPAT_VERSION"))
1679 t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")) << ";\n";
1680 if (project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
1681 project->isActiveConfig("lib_bundle"))
1682 t << "\t\t\t\t" << writeSettings("FRAMEWORK_VERSION", project->first("QMAKE_FRAMEWORK_VERSION")) << ";\n";
1683 }
1684 if (!project->isEmpty("COMPAT_VERSION"))
1685 t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("COMPAT_VERSION")) << ";\n";
1686
1687 if (!project->isEmpty("QMAKE_MACOSX_DEPLOYMENT_TARGET"))
1688 t << "\t\t\t\t" << writeSettings("MACOSX_DEPLOYMENT_TARGET", project->first("QMAKE_MACOSX_DEPLOYMENT_TARGET")) << ";\n";
1689 if (!project->isEmpty("QMAKE_IOS_DEPLOYMENT_TARGET"))
1690 t << "\t\t\t\t" << writeSettings("IPHONEOS_DEPLOYMENT_TARGET", project->first("QMAKE_IOS_DEPLOYMENT_TARGET")) << ";\n";
1691 if (!project->isEmpty("QMAKE_TVOS_DEPLOYMENT_TARGET"))
1692 t << "\t\t\t\t" << writeSettings("APPLETVOS_DEPLOYMENT_TARGET", project->first("QMAKE_TVOS_DEPLOYMENT_TARGET")) << ";\n";
1693 if (!project->isEmpty("QMAKE_WATCHOS_DEPLOYMENT_TARGET"))
1694 t << "\t\t\t\t" << writeSettings("WATCHOS_DEPLOYMENT_TARGET", project->first("QMAKE_WATCHOS_DEPLOYMENT_TARGET")) << ";\n";
1695
1696 if (!project->isEmpty("QMAKE_XCODE_CODE_SIGN_IDENTITY"))
1697 t << "\t\t\t\t" << writeSettings("CODE_SIGN_IDENTITY", project->first("QMAKE_XCODE_CODE_SIGN_IDENTITY")) << ";\n";
1698
1699 tmp = project->values("QMAKE_PBX_VARS");
1700 for (int i = 0; i < tmp.count(); i++) {
1701 QString var = tmp[i].toQString(), val = QString::fromLocal8Bit(qgetenv(var.toLatin1().constData()));
1702 if (val.isEmpty() && var == "TB")
1703 val = "/usr/bin/";
1704 t << "\t\t\t\t" << writeSettings(var, val) << ";\n";
1705 }
1706 if (!project->isEmpty("PRECOMPILED_HEADER")) {
1707 t << "\t\t\t\t" << writeSettings("GCC_PRECOMPILE_PREFIX_HEADER", "YES") << ";\n"
1708 << "\t\t\t\t" << writeSettings("GCC_PREFIX_HEADER", project->first("PRECOMPILED_HEADER")) << ";\n";
1709 }
1710 t << "\t\t\t\t" << writeSettings("HEADER_SEARCH_PATHS", fixListForOutput("INCLUDEPATH"), SettingsAsList, 5) << ";\n"
1711 << "\t\t\t\t" << writeSettings("LIBRARY_SEARCH_PATHS", fixListForOutput("QMAKE_PBX_LIBPATHS"), SettingsAsList, 5) << ";\n"
1712 << "\t\t\t\t" << writeSettings("FRAMEWORK_SEARCH_PATHS", fixListForOutput("QMAKE_FRAMEWORKPATH"),
1713 !project->values("QMAKE_FRAMEWORKPATH").isEmpty() ? SettingsAsList : 0, 5) << ";\n";
1714
1715 {
1716 ProStringList cflags = project->values("QMAKE_CFLAGS");
1717 const ProStringList &prl_defines = project->values("PRL_EXPORT_DEFINES");
1718 for (int i = 0; i < prl_defines.size(); ++i)
1719 cflags += "-D" + prl_defines.at(i);
1720 const ProStringList &defines = project->values("DEFINES");
1721 for (int i = 0; i < defines.size(); ++i)
1722 cflags += "-D" + defines.at(i);
1723 t << "\t\t\t\t" << writeSettings("OTHER_CFLAGS", fixListForOutput(cflags), SettingsAsList, 5) << ";\n";
1724 }
1725 {
1726 ProStringList cxxflags = project->values("QMAKE_CXXFLAGS");
1727 const ProStringList &prl_defines = project->values("PRL_EXPORT_DEFINES");
1728 for (int i = 0; i < prl_defines.size(); ++i)
1729 cxxflags += "-D" + prl_defines.at(i);
1730 const ProStringList &defines = project->values("DEFINES");
1731 for (int i = 0; i < defines.size(); ++i)
1732 cxxflags += "-D" + defines.at(i);
1733 t << "\t\t\t\t" << writeSettings("OTHER_CPLUSPLUSFLAGS", fixListForOutput(cxxflags), SettingsAsList, 5) << ";\n";
1734 }
1735 if (!project->isActiveConfig("staticlib")) {
1736 t << "\t\t\t\t" << writeSettings("OTHER_LDFLAGS",
1737 fixListForOutput("SUBLIBS")
1738 + fixListForOutput("QMAKE_LFLAGS")
1739 + fixListForOutput(fixLibFlags("LIBS"))
1740 + fixListForOutput(fixLibFlags("LIBS_PRIVATE"))
1741 + fixListForOutput(fixLibFlags("QMAKE_LIBS"))
1742 + fixListForOutput(fixLibFlags("QMAKE_LIBS_PRIVATE")),
1743 SettingsAsList, 6) << ";\n";
1744 }
1745 const ProStringList &archs = !project->values("QMAKE_XCODE_ARCHS").isEmpty() ?
1746 project->values("QMAKE_XCODE_ARCHS") : project->values("QT_ARCH");
1747 if (!archs.isEmpty())
1748 t << "\t\t\t\t" << writeSettings("ARCHS", archs) << ";\n";
1749 if (!project->isEmpty("OBJECTS_DIR"))
1750 t << "\t\t\t\t" << writeSettings("OBJROOT", project->first("OBJECTS_DIR")) << ";\n";
1751 } else {
1752 if (project->first("TEMPLATE") == "app") {
1753 t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET").toQString())) << ";\n";
1754 } else {
1755 if (!project->isActiveConfig("plugin") && project->isActiveConfig("staticlib"))
1756 t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "STATIC") << ";\n";
1757 else
1758 t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "DYNAMIC") << ";\n";
1759 ProString lib = project->first("QMAKE_ORIG_TARGET");
1760 if (!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib"))
1761 lib.prepend("lib");
1762 t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", lib) << ";\n";
1763 }
1764 }
1765 t << "\t\t\t};\n"
1766 << "\t\t\t" << writeSettings("name", configName) << ";\n"
1767 << "\t\t};\n";
1768 }
1769 }
1770 for (int i = 0; i < buildConfigGroups.size(); i++) {
1771 t << "\t\t" << keyFor("QMAKE_PBX_BUILDCONFIG_LIST_" + buildConfigGroups.at(i)) << " = {\n"
1772 << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";\n"
1773 << "\t\t\t" << writeSettings("buildConfigurations", project->values(ProKey("QMAKE_PBX_BUILDCONFIGS_" + buildConfigGroups.at(i))), SettingsAsList, 4) << ";\n"
1774 << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";\n"
1775 << "\t\t\t" << writeSettings("defaultConfigurationName", defaultConfig) << ";\n"
1776 << "\t\t};\n";
1777 }
1778 //ROOT
1779 t << "\t\t" << keyFor("QMAKE_PBX_ROOT") << " = {\n"
1780 << "\t\t\t" << writeSettings("hasScannedForEncodings", "1", SettingsNoQuote) << ";\n"
1781 << "\t\t\t" << writeSettings("compatibilityVersion", "Xcode 3.2") << ";\n"
1782 << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";\n"
1783 << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_PBX_ROOT_GROUP")) << ";\n"
1784 << "\t\t\t" << writeSettings("productRefGroup", keyFor("Products")) << ";\n";
1785 t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST_PROJECT")) << ";\n";
1786 t << "\t\t\t" << writeSettings("projectDirPath", ProStringList()) << ";\n"
1787 << "\t\t\t" << writeSettings("projectRoot", "") << ";\n"
1788 << "\t\t\t" << writeSettings("targets", project->values("QMAKE_PBX_TARGETS"), SettingsAsList, 4) << ";\n"
1789 << "\t\t\t" << "attributes = {\n"
1790 << "\t\t\t\tTargetAttributes = {\n";
1791 for (const ProString &target : project->values("QMAKE_PBX_TARGETS")) {
1792 const ProStringList &attributes = project->values(ProKey("QMAKE_PBX_TARGET_ATTRIBUTES_" + target));
1793 if (attributes.isEmpty())
1794 continue;
1795 t << "\t\t\t\t\t" << target << " = {\n";
1796 for (const ProString &attribute : attributes)
1797 t << "\t\t\t\t\t\t" << writeSettings(attribute.toQString(), project->first(ProKey("QMAKE_PBX_TARGET_ATTRIBUTES_" + target + "_" + attribute))) << ";\n";
1798 t << "\t\t\t\t\t};\n";
1799 }
1800 t << "\t\t\t\t};\n"
1801 << "\t\t\t};\n"
1802 << "\t\t};\n";
1803
1804 // FIXME: Deal with developmentRegion and knownRegions for QMAKE_PBX_ROOT
1805
1806 //FOOTER
1807 t << "\t};\n"
1808 << "\t" << writeSettings("rootObject", keyFor("QMAKE_PBX_ROOT")) << ";\n"
1809 << "}\n";
1810
1811 // Scheme
1812 {
1813 QString xcodeSpecDir = project->first("QMAKE_XCODE_SPECDIR").toQString();
1814
1815 bool wroteCustomScheme = false;
1816
1817 QString projectSharedSchemesPath = pbx_dir + "/xcshareddata/xcschemes";
1818 if (mkdir(projectSharedSchemesPath)) {
1819 QString target = project->first("QMAKE_ORIG_TARGET").toQString();
1820
1821 QFile defaultSchemeFile(xcodeSpecDir + "/default.xcscheme");
1822 QFile outputSchemeFile(projectSharedSchemesPath + Option::dir_sep + target + ".xcscheme");
1823
1824 if (defaultSchemeFile.open(QIODevice::ReadOnly)
1825 && outputSchemeFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1826
1827 QTextStream defaultSchemeStream(&defaultSchemeFile);
1828 QString schemeData = defaultSchemeStream.readAll();
1829
1830 schemeData.replace(QLatin1String("@QMAKE_ORIG_TARGET@"), target);
1831 schemeData.replace(QLatin1String("@TARGET_PBX_KEY@"), keyFor(pbx_dir + "QMAKE_PBX_TARGET"));
1832 schemeData.replace(QLatin1String("@TEST_BUNDLE_PBX_KEY@"), keyFor("QMAKE_TEST_BUNDLE_REFERENCE"));
1833 schemeData.replace(QLatin1String("@QMAKE_RELATIVE_PBX_DIR@"), fileFixify(pbx_dir));
1834
1835 QTextStream outputSchemeStream(&outputSchemeFile);
1836 outputSchemeStream << schemeData;
1837
1838 wroteCustomScheme = true;
1839 }
1840 }
1841
1842 if (wroteCustomScheme) {
1843 // Prevent Xcode from auto-generating schemes
1844 QString workspaceSettingsFilename("WorkspaceSettings.xcsettings");
1845 QString workspaceSharedDataPath = pbx_dir + "/project.xcworkspace/xcshareddata";
1846 if (mkdir(workspaceSharedDataPath)) {
1847 QFile::copy(xcodeSpecDir + Option::dir_sep + workspaceSettingsFilename,
1848 workspaceSharedDataPath + Option::dir_sep + workspaceSettingsFilename);
1849 } else {
1850 wroteCustomScheme = false;
1851 }
1852 }
1853
1854 if (!wroteCustomScheme)
1855 warn_msg(WarnLogic, "Failed to generate schemes in '%s', " \
1856 "falling back to Xcode auto-generated schemes", qPrintable(projectSharedSchemesPath));
1857 }
1858
1859 return true;
1860 }
1861
1862 QString
findProgram(const ProString & prog)1863 ProjectBuilderMakefileGenerator::findProgram(const ProString &prog)
1864 {
1865 QString ret = prog.toQString();
1866 if(QDir::isRelativePath(ret)) {
1867 QStringList paths = QString(qgetenv("PATH")).split(':');
1868 for(int i = 0; i < paths.size(); ++i) {
1869 QString path = paths.at(i) + "/" + prog;
1870 if(exists(path)) {
1871 ret = path;
1872 break;
1873 }
1874 }
1875 }
1876 return ret;
1877 }
1878
1879 QString
fixForOutput(const QString & values)1880 ProjectBuilderMakefileGenerator::fixForOutput(const QString &values)
1881 {
1882 //get the environment variables references
1883 QRegExp reg_var("\\$\\((.*)\\)");
1884 for(int rep = 0; (rep = reg_var.indexIn(values, rep)) != -1;) {
1885 if(project->values("QMAKE_PBX_VARS").indexOf(reg_var.cap(1)) == -1)
1886 project->values("QMAKE_PBX_VARS").append(reg_var.cap(1));
1887 rep += reg_var.matchedLength();
1888 }
1889
1890 return values;
1891 }
1892
1893 ProStringList
fixListForOutput(const char * where)1894 ProjectBuilderMakefileGenerator::fixListForOutput(const char *where)
1895 {
1896 return fixListForOutput(project->values(where));
1897 }
1898
1899 ProStringList
fixListForOutput(const ProStringList & l)1900 ProjectBuilderMakefileGenerator::fixListForOutput(const ProStringList &l)
1901 {
1902 ProStringList ret;
1903 for(int i = 0; i < l.count(); i++)
1904 ret += fixForOutput(l[i].toQString());
1905 return ret;
1906 }
1907
1908 QString
keyFor(const QString & block)1909 ProjectBuilderMakefileGenerator::keyFor(const QString &block)
1910 {
1911 #if 1 //This make this code much easier to debug..
1912 if(project->isActiveConfig("no_pb_munge_key"))
1913 return block;
1914 #endif
1915 QString ret;
1916 if(!keys.contains(block)) {
1917 ret = qtSha1(block.toUtf8()).left(24).toUpper();
1918 keys.insert(block, ret);
1919 } else {
1920 ret = keys[block];
1921 }
1922 return ret;
1923 }
1924
1925 bool
openOutput(QFile & file,const QString & build) const1926 ProjectBuilderMakefileGenerator::openOutput(QFile &file, const QString &build) const
1927 {
1928 Q_ASSERT_X(QDir::isRelativePath(file.fileName()), "ProjectBuilderMakefileGenerator",
1929 "runQMake() should have normalized the filename and made it relative");
1930
1931 QFileInfo fi(fileInfo(file.fileName()));
1932 if (fi.suffix() != "pbxproj") {
1933 QString output = file.fileName();
1934 if (!output.endsWith(projectSuffix())) {
1935 if (fi.fileName().isEmpty()) {
1936 if (project->first("TEMPLATE") == "subdirs" || project->isEmpty("QMAKE_ORIG_TARGET"))
1937 output += fileInfo(project->projectFile()).baseName();
1938 else
1939 output += project->first("QMAKE_ORIG_TARGET").toQString();
1940 }
1941 output += projectSuffix() + QDir::separator();
1942 } else {
1943 output += QDir::separator();
1944 }
1945 output += QString("project.pbxproj");
1946 file.setFileName(output);
1947 }
1948
1949 pbx_dir = Option::output_dir + Option::dir_sep + file.fileName().section(Option::dir_sep, 0, 0);
1950 return UnixMakefileGenerator::openOutput(file, build);
1951 }
1952
1953 int
pbuilderVersion() const1954 ProjectBuilderMakefileGenerator::pbuilderVersion() const
1955 {
1956 if (!project->isEmpty("QMAKE_PBUILDER_VERSION"))
1957 return project->first("QMAKE_PBUILDER_VERSION").toInt();
1958 return 46; // Xcode 3.2-compatible; default format since that version
1959 }
1960
1961 int
reftypeForFile(const QString & where)1962 ProjectBuilderMakefileGenerator::reftypeForFile(const QString &where)
1963 {
1964 int ret = 0; //absolute is the default..
1965 if (QDir::isRelativePath(where))
1966 ret = 4; //relative
1967 return ret;
1968 }
1969
1970 QString
projectSuffix() const1971 ProjectBuilderMakefileGenerator::projectSuffix() const
1972 {
1973 return ".xcodeproj";
1974 }
1975
1976 QString
pbxbuild()1977 ProjectBuilderMakefileGenerator::pbxbuild()
1978 {
1979 return "xcodebuild";
1980 }
1981
quotedStringLiteral(const QString & value)1982 static QString quotedStringLiteral(const QString &value)
1983 {
1984 QString result;
1985 const int len = value.length();
1986 result.reserve(int(len * 1.1) + 2);
1987
1988 result += QLatin1Char('"');
1989
1990 // Escape
1991 for (int i = 0; i < len; ++i) {
1992 QChar character = value.at(i);;
1993 ushort code = character.unicode();
1994 switch (code) {
1995 case '\\':
1996 result += QLatin1String("\\\\");
1997 break;
1998 case '"':
1999 result += QLatin1String("\\\"");
2000 break;
2001 case '\b':
2002 result += QLatin1String("\\b");
2003 break;
2004 case '\n':
2005 result += QLatin1String("\\n");
2006 break;
2007 case '\r':
2008 result += QLatin1String("\\r");
2009 break;
2010 case '\t':
2011 result += QLatin1String("\\t");
2012 break;
2013 default:
2014 if (code >= 32 && code <= 127)
2015 result += character;
2016 else
2017 result += QLatin1String("\\u") + QString::number(code, 16).rightJustified(4, '0');
2018 }
2019 }
2020
2021 result += QLatin1Char('"');
2022
2023 result.squeeze();
2024 return result;
2025 }
2026
2027 QString
writeSettings(const QString & var,const ProStringList & vals,int flags,int indent_level)2028 ProjectBuilderMakefileGenerator::writeSettings(const QString &var, const ProStringList &vals, int flags, int indent_level)
2029 {
2030 QString ret;
2031 bool shouldQuote = !((flags & SettingsNoQuote));
2032
2033 QString newline = "\n";
2034 for(int i = 0; i < indent_level; ++i)
2035 newline += "\t";
2036
2037 static QRegExp allowedVariableCharacters("^[a-zA-Z0-9_]*$");
2038 ret += var.contains(allowedVariableCharacters) ? var : quotedStringLiteral(var);
2039
2040 ret += " = ";
2041
2042 if(flags & SettingsAsList) {
2043 ret += "(" + newline;
2044 for(int i = 0, count = 0; i < vals.size(); ++i) {
2045 QString val = vals.at(i).toQString();
2046 if(!val.isEmpty()) {
2047 if(count++ > 0)
2048 ret += "," + newline;
2049 if (shouldQuote)
2050 val = quotedStringLiteral(val);
2051 ret += val;
2052 }
2053 }
2054 ret += ")";
2055 } else {
2056 QString val = vals.join(QLatin1Char(' '));
2057 if (shouldQuote)
2058 val = quotedStringLiteral(val);
2059 ret += val;
2060 }
2061 return ret;
2062 }
2063
2064 QT_END_NAMESPACE
2065