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

source code of qtbase/qmake/generators/mac/pbuilder_pbx.cpp