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 | |