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

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