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