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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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