|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include <QString> |
|
42 #include <QStringList> |
|
43 #include <QDebug> |
|
44 #include <iostream> |
|
45 #include <QProcess> |
|
46 #include <QDir> |
|
47 #include <QRegExp> |
|
48 #include <QSet> |
|
49 #include <QDirIterator> |
|
50 #include "shared.h" |
|
51 |
|
52 bool runStripEnabled = true; |
|
53 int logLevel = 1; |
|
54 |
|
55 using std::cout; |
|
56 using std::endl; |
|
57 |
|
58 bool operator==(const FrameworkInfo &a, const FrameworkInfo &b) |
|
59 { |
|
60 return ((a.frameworkPath == b.frameworkPath) && (a.binaryPath == b.binaryPath)); |
|
61 } |
|
62 |
|
63 QDebug operator<<(QDebug debug, const FrameworkInfo &info) |
|
64 { |
|
65 debug << "Framework name" << info.frameworkName << "\n"; |
|
66 debug << "Framework directory" << info.frameworkDirectory << "\n"; |
|
67 debug << "Framework path" << info.frameworkPath << "\n"; |
|
68 debug << "Binary directory" << info.binaryDirectory << "\n"; |
|
69 debug << "Binary name" << info.binaryName << "\n"; |
|
70 debug << "Binary path" << info.binaryPath << "\n"; |
|
71 debug << "Version" << info.version << "\n"; |
|
72 debug << "Install name" << info.installName << "\n"; |
|
73 debug << "Deployed install name" << info.deployedInstallName << "\n"; |
|
74 debug << "Source file Path" << info.sourceFilePath << "\n"; |
|
75 debug << "Deployed Directory (relative to bundle)" << info.destinationDirectory << "\n"; |
|
76 |
|
77 return debug; |
|
78 } |
|
79 |
|
80 const QString bundleFrameworkDirectory = "Contents/Frameworks"; |
|
81 const QString bundleBinaryDirectory = "Contents/MacOS"; |
|
82 |
|
83 inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info) |
|
84 { |
|
85 debug << "Application bundle path" << info.path << "\n"; |
|
86 debug << "Binary path" << info.binaryPath << "\n"; |
|
87 return debug; |
|
88 } |
|
89 |
|
90 bool copyFilePrintStatus(const QString &from, const QString &to) |
|
91 { |
|
92 if (QFile::copy(from, to)) { |
|
93 LogNormal() << " copied:" << from; |
|
94 LogNormal() << " to" << to; |
|
95 return true; |
|
96 } else { |
|
97 LogError() << "file copy failed from" << from; |
|
98 LogError() << " to" << to; |
|
99 return false; |
|
100 } |
|
101 } |
|
102 |
|
103 FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs) |
|
104 { |
|
105 FrameworkInfo info; |
|
106 QString trimmed = line.trimmed(); |
|
107 |
|
108 if (trimmed.isEmpty()) |
|
109 return info; |
|
110 |
|
111 // Don't deploy system libraries. |
|
112 if (trimmed.startsWith("/System/Library/") || |
|
113 (trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene |
|
114 || trimmed.startsWith("@executable_path")) |
|
115 return info; |
|
116 |
|
117 enum State {QtPath, FrameworkName, DylibName, Version, End}; |
|
118 State state = QtPath; |
|
119 int part = 0; |
|
120 QString name; |
|
121 QString qtPath; |
|
122 QString suffix = useDebugLibs ? "_debug" : ""; |
|
123 |
|
124 // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/ |
|
125 QStringList parts = trimmed.split("/"); |
|
126 while (part < parts.count()) { |
|
127 const QString currentPart = parts.at(part).simplified() ; |
|
128 ++part; |
|
129 if (currentPart == "") |
|
130 continue; |
|
131 |
|
132 if (state == QtPath) { |
|
133 // Check for library name part |
|
134 if (part < parts.count() && parts.at(part).contains(".dylib ")) { |
|
135 state = DylibName; |
|
136 info.installName += "/" + (qtPath + "lib/").simplified(); |
|
137 info.frameworkDirectory = info.installName; |
|
138 state = DylibName; |
|
139 continue; |
|
140 } else if (part < parts.count() && parts.at(part).endsWith(".framework")) { |
|
141 info.installName += "/" + (qtPath + "lib/").simplified(); |
|
142 info.frameworkDirectory = info.installName; |
|
143 state = FrameworkName; |
|
144 continue; |
|
145 } else if (trimmed.startsWith("/") == false) { // If the line does not contain a full path, the app is using a binary Qt package. |
|
146 if (currentPart.contains(".framework")) { |
|
147 info.frameworkDirectory = "/Library/Frameworks/"; |
|
148 state = FrameworkName; |
|
149 } else { |
|
150 info.frameworkDirectory = "/usr/lib/"; |
|
151 state = DylibName; |
|
152 } |
|
153 |
|
154 --part; |
|
155 continue; |
|
156 } |
|
157 qtPath += (currentPart + "/"); |
|
158 |
|
159 } if (state == FrameworkName) { |
|
160 // remove ".framework" |
|
161 name = currentPart; |
|
162 name.chop(QString(".framework").length()); |
|
163 info.frameworkName = currentPart; |
|
164 state = Version; |
|
165 ++part; |
|
166 continue; |
|
167 } if (state == DylibName) { |
|
168 name = currentPart.split(" (compatibility").at(0); |
|
169 info.frameworkName = name; |
|
170 info.binaryName = name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.')); |
|
171 info.installName += name; |
|
172 info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName; |
|
173 info.frameworkPath = info.frameworkDirectory + info.binaryName; |
|
174 info.sourceFilePath = info.frameworkPath; |
|
175 info.destinationDirectory = bundleFrameworkDirectory + "/"; |
|
176 info.binaryDirectory = info.frameworkDirectory; |
|
177 info.binaryPath = info.frameworkPath; |
|
178 state = End; |
|
179 ++part; |
|
180 continue; |
|
181 } else if (state == Version) { |
|
182 info.version = currentPart; |
|
183 info.binaryDirectory = "Versions/" + info.version; |
|
184 info.binaryName = name + suffix; |
|
185 info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName; |
|
186 info.installName += info.frameworkName + "/" + info.binaryDirectory + "/" + name; |
|
187 info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath; |
|
188 info.frameworkPath = info.frameworkDirectory + info.frameworkName; |
|
189 info.sourceFilePath = info.frameworkPath + info.binaryPath; |
|
190 info.destinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName + "/" + info.binaryDirectory; |
|
191 state = End; |
|
192 } else if (state == End) { |
|
193 break; |
|
194 } |
|
195 } |
|
196 |
|
197 return info; |
|
198 } |
|
199 |
|
200 QString findAppBinary(const QString &appBundlePath) |
|
201 { |
|
202 QString appName = QFileInfo(appBundlePath).completeBaseName(); |
|
203 QString binaryPath = appBundlePath + "/Contents/MacOS/" + appName; |
|
204 |
|
205 if (QFile::exists(binaryPath)) |
|
206 return binaryPath; |
|
207 LogError() << "Could not find bundle binary for" << appBundlePath; |
|
208 return QString(); |
|
209 } |
|
210 |
|
211 QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs) |
|
212 { |
|
213 QList<FrameworkInfo> libraries; |
|
214 foreach(const QString line, otoolLines) { |
|
215 FrameworkInfo info = parseOtoolLibraryLine(line, useDebugLibs); |
|
216 if (info.frameworkName.isEmpty() == false) { |
|
217 LogDebug() << "Adding framework:"; |
|
218 LogDebug() << info; |
|
219 libraries.append(info); |
|
220 } |
|
221 } |
|
222 return libraries; |
|
223 } |
|
224 |
|
225 QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs) |
|
226 { |
|
227 LogDebug() << "Using otool:"; |
|
228 LogDebug() << " inspecting" << path; |
|
229 QProcess otool; |
|
230 otool.start("otool", QStringList() << "-L" << path); |
|
231 otool.waitForFinished(); |
|
232 |
|
233 if (otool.exitCode() != 0) { |
|
234 LogError() << otool.readAllStandardError(); |
|
235 } |
|
236 |
|
237 QString output = otool.readAllStandardOutput(); |
|
238 QStringList outputLines = output.split("\n"); |
|
239 outputLines.removeFirst(); // remove line containing the binary path |
|
240 if (path.contains(".framework") || path.contains(".dylib")) |
|
241 outputLines.removeFirst(); // frameworks and dylibs lists themselves as a dependency. |
|
242 |
|
243 return getQtFrameworks(outputLines, useDebugLibs); |
|
244 } |
|
245 |
|
246 // copies everything _inside_ sourcePath to destinationPath |
|
247 void recursiveCopy(const QString &sourcePath, const QString &destinationPath) |
|
248 { |
|
249 QDir().mkpath(destinationPath); |
|
250 |
|
251 QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot); |
|
252 foreach (QString file, files) { |
|
253 const QString fileSourcePath = sourcePath + "/" + file; |
|
254 const QString fileDestinationPath = destinationPath + "/" + file; |
|
255 copyFilePrintStatus(fileSourcePath, fileDestinationPath); |
|
256 } |
|
257 |
|
258 QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); |
|
259 foreach (QString dir, subdirs) { |
|
260 recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir); |
|
261 } |
|
262 } |
|
263 |
|
264 QString copyFramework(const FrameworkInfo &framework, const QString path) |
|
265 { |
|
266 QString from = framework.sourceFilePath; |
|
267 QString toDir = path + "/" + framework.destinationDirectory; |
|
268 QString to = toDir + "/" + framework.binaryName; |
|
269 |
|
270 if (QFile::exists(from) == false) { |
|
271 LogError() << "no file at" << from; |
|
272 return QString(); |
|
273 } |
|
274 |
|
275 |
|
276 QDir dir; |
|
277 if (dir.mkpath(toDir) == false) { |
|
278 LogError() << "could not create destination directory" << to; |
|
279 return QString(); |
|
280 } |
|
281 |
|
282 |
|
283 if (QFile::exists(to)) { |
|
284 return QString(); |
|
285 } |
|
286 |
|
287 copyFilePrintStatus(from, to); |
|
288 |
|
289 const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; |
|
290 const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; |
|
291 recursiveCopy(resourcesSourcePath, resourcesDestianationPath); |
|
292 |
|
293 return to; |
|
294 } |
|
295 |
|
296 void runInstallNameTool(QStringList options) |
|
297 { |
|
298 QProcess installNametool; |
|
299 installNametool.start("install_name_tool", options); |
|
300 installNametool.waitForFinished(); |
|
301 if (installNametool.exitCode() != 0) { |
|
302 LogError() << installNametool.readAllStandardError(); |
|
303 LogError() << installNametool.readAllStandardOutput(); |
|
304 } |
|
305 } |
|
306 |
|
307 void changeIdentification(const QString &id, const QString &binaryPath) |
|
308 { |
|
309 LogDebug() << "Using install_name_tool:"; |
|
310 LogDebug() << " change identification in" << binaryPath; |
|
311 LogDebug() << " to" << id; |
|
312 runInstallNameTool(QStringList() << "-id" << id << binaryPath); |
|
313 } |
|
314 |
|
315 void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath) |
|
316 { |
|
317 LogDebug() << "Using install_name_tool:"; |
|
318 LogDebug() << " in" << binaryPath; |
|
319 LogDebug() << " change reference" << oldName; |
|
320 LogDebug() << " to" << newName; |
|
321 runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath); |
|
322 } |
|
323 |
|
324 void runStrip(const QString &binaryPath) |
|
325 { |
|
326 if (runStripEnabled == false) |
|
327 return; |
|
328 |
|
329 LogDebug() << "Using strip:"; |
|
330 LogDebug() << " stripped" << binaryPath; |
|
331 QProcess strip; |
|
332 strip.start("strip", QStringList() << "-x" << binaryPath); |
|
333 strip.waitForFinished(); |
|
334 if (strip.exitCode() != 0) { |
|
335 LogError() << strip.readAllStandardError(); |
|
336 LogError() << strip.readAllStandardOutput(); |
|
337 } |
|
338 } |
|
339 |
|
340 /* |
|
341 Deploys the the listed frameworks listed into an app bundle. |
|
342 The frameworks are searched for dependencies, which are also deployed. |
|
343 (deploying Qt3Support will also deploy QtNetwork and QtSql for example.) |
|
344 Returns a DeploymentInfo structure containing the Qt path used and a |
|
345 a list of actually deployed frameworks. |
|
346 */ |
|
347 DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, |
|
348 const QString &bundlePath, const QString &binaryPath, bool useDebugLibs) |
|
349 { |
|
350 LogNormal(); |
|
351 LogNormal() << "Deploying Qt frameworks found inside:" << binaryPath; |
|
352 QStringList copiedFrameworks; |
|
353 DeploymentInfo deploymenInfo; |
|
354 |
|
355 while (frameworks.isEmpty() == false) { |
|
356 const FrameworkInfo framework = frameworks.takeFirst(); |
|
357 copiedFrameworks.append(framework.frameworkName); |
|
358 |
|
359 // Get the qt path from one of the Qt frameworks; |
|
360 if (deploymenInfo.qtPath.isNull() && framework.frameworkName.contains("Qt") |
|
361 && framework.frameworkDirectory.contains("/lib")) |
|
362 { |
|
363 deploymenInfo.qtPath = framework.frameworkDirectory; |
|
364 deploymenInfo.qtPath.chop(5); // remove "/lib/" |
|
365 } |
|
366 |
|
367 if (framework.installName.startsWith("/@executable_path/")) { |
|
368 LogError() << framework.frameworkName << "already deployed, skipping."; |
|
369 continue; |
|
370 } |
|
371 |
|
372 // Install_name_tool the new id into the binary |
|
373 changeInstallName(framework.installName, framework.deployedInstallName, binaryPath); |
|
374 |
|
375 // Copy farmework to app bundle. |
|
376 const QString deployedBinaryPath = copyFramework(framework, bundlePath); |
|
377 // Skip the rest if already was deployed. |
|
378 if (deployedBinaryPath.isNull()) |
|
379 continue; |
|
380 |
|
381 runStrip(deployedBinaryPath); |
|
382 |
|
383 // Install_name_tool it a new id. |
|
384 changeIdentification(framework.deployedInstallName, deployedBinaryPath); |
|
385 // Check for framework dependencies |
|
386 QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs); |
|
387 |
|
388 foreach (FrameworkInfo dependency, dependencies) { |
|
389 changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath); |
|
390 |
|
391 // Deploy framework if neccesary. |
|
392 if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) { |
|
393 frameworks.append(dependency); |
|
394 } |
|
395 } |
|
396 } |
|
397 deploymenInfo.deployedFrameworks = copiedFrameworks; |
|
398 return deploymenInfo; |
|
399 } |
|
400 |
|
401 DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs) |
|
402 { |
|
403 ApplicationBundleInfo applicationBundle; |
|
404 applicationBundle.path = appBundlePath; |
|
405 applicationBundle.binaryPath = findAppBinary(appBundlePath); |
|
406 QList<FrameworkInfo> frameworks = getQtFrameworks(applicationBundle.binaryPath, useDebugLibs); |
|
407 if (frameworks.isEmpty()) { |
|
408 LogWarning(); |
|
409 LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath; |
|
410 LogWarning() << "Perhaps macdeployqt was already used on" << appBundlePath << "?"; |
|
411 LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again."; |
|
412 return DeploymentInfo(); |
|
413 } else { |
|
414 return deployQtFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, useDebugLibs); |
|
415 } |
|
416 } |
|
417 |
|
418 void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, |
|
419 const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs) |
|
420 { |
|
421 LogNormal() << "Deploying plugins from" << pluginSourcePath; |
|
422 QStringList plugins = QDir(pluginSourcePath).entryList(QStringList() << "*.dylib"); |
|
423 |
|
424 foreach (QString pluginName, plugins) { |
|
425 if (pluginSourcePath.contains(deploymentInfo.pluginPath)) { |
|
426 QStringList deployedFrameworks = deploymentInfo.deployedFrameworks; |
|
427 |
|
428 // Skip the debug versions of the plugins, unless specified otherwise. |
|
429 if (!useDebugLibs && pluginName.endsWith("_debug.dylib")) |
|
430 continue; |
|
431 |
|
432 // Skip the release versions of the plugins, unless specified otherwise. |
|
433 if (useDebugLibs && !pluginName.endsWith("_debug.dylib")) |
|
434 continue; |
|
435 |
|
436 // Skip the designer plugins |
|
437 if (pluginSourcePath.contains("plugins/designer")) |
|
438 continue; |
|
439 |
|
440 #ifndef QT_GRAPHICSSYSTEM_OPENGL |
|
441 // SKip the opengl graphicssystem plugin when not in use. |
|
442 if (pluginName.contains("libqglgraphicssystem")) |
|
443 continue; |
|
444 #endif |
|
445 // Deploy accessibility for Qt3Support only if the Qt3Support.framework is in use |
|
446 if (deployedFrameworks.indexOf("Qt3Support.framework") == -1 && pluginName.contains("accessiblecompatwidgets")) |
|
447 continue; |
|
448 |
|
449 // Deploy the svg icon plugin if QtSvg.framework is in use. |
|
450 if (deployedFrameworks.indexOf("QtSvg.framework") == -1 && pluginName.contains("svg")) |
|
451 continue; |
|
452 |
|
453 // Deploy the phonon plugins if phonon.framework is in use |
|
454 if (deployedFrameworks.indexOf("phonon.framework") == -1 && pluginName.contains("phonon")) |
|
455 continue; |
|
456 |
|
457 // Deploy the sql plugins if QtSql.framework is in use |
|
458 if (deployedFrameworks.indexOf("QtSql.framework") == -1 && pluginName.contains("sql")) |
|
459 continue; |
|
460 |
|
461 // Deploy the script plugins if QtScript.framework is in use |
|
462 if (deployedFrameworks.indexOf("QtScript.framework") == -1 && pluginName.contains("script")) |
|
463 continue; |
|
464 } |
|
465 |
|
466 QDir dir; |
|
467 dir.mkpath(pluginDestinationPath); |
|
468 |
|
469 const QString sourcePath = pluginSourcePath + "/" + pluginName; |
|
470 const QString destinationPath = pluginDestinationPath + "/" + pluginName; |
|
471 if (copyFilePrintStatus(sourcePath, destinationPath)) { |
|
472 |
|
473 runStrip(destinationPath); |
|
474 |
|
475 // Special case for the phonon plugin: CoreVideo is not available as a separate framework |
|
476 // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) |
|
477 if (pluginName.contains("libphonon_qt7")) { |
|
478 changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", |
|
479 "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", |
|
480 destinationPath); |
|
481 } |
|
482 |
|
483 QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs); |
|
484 deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath, useDebugLibs); |
|
485 } |
|
486 } // foreach plugins |
|
487 |
|
488 QStringList subdirs = QDir(pluginSourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); |
|
489 foreach (const QString &subdir, subdirs) |
|
490 deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo, useDebugLibs); |
|
491 } |
|
492 |
|
493 void createQtConf(const QString &appBundlePath) |
|
494 { |
|
495 QByteArray contents = "[Paths]\nPlugins = PlugIns\n"; |
|
496 QString filePath = appBundlePath + "/Contents/Resources/"; |
|
497 QString fileName = filePath + "qt.conf"; |
|
498 |
|
499 QDir().mkpath(filePath); |
|
500 |
|
501 QFile qtconf(fileName); |
|
502 if (qtconf.exists()) { |
|
503 LogWarning(); |
|
504 LogWarning() << fileName << "already exists, will not overwrite."; |
|
505 LogWarning() << "To make sure the plugins are loaded from the correct location,"; |
|
506 LogWarning() << "please make sure qt.conf contains the following lines:"; |
|
507 LogWarning() << "[Paths]"; |
|
508 LogWarning() << " Plugins = PlugIns"; |
|
509 return; |
|
510 } |
|
511 |
|
512 qtconf.open(QIODevice::WriteOnly); |
|
513 if (qtconf.write(contents) != -1) { |
|
514 LogNormal() << "Created configuration file:" << fileName; |
|
515 LogNormal() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; |
|
516 } |
|
517 } |
|
518 |
|
519 void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs) |
|
520 { |
|
521 ApplicationBundleInfo applicationBundle; |
|
522 applicationBundle.path = appBundlePath; |
|
523 applicationBundle.binaryPath = findAppBinary(appBundlePath); |
|
524 |
|
525 const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns"; |
|
526 deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs); |
|
527 } |
|
528 |
|
529 |
|
530 void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &absoluteQtPath) |
|
531 { |
|
532 LogNormal() << "Changing" << appBinaryPath << "to link against"; |
|
533 LogNormal() << "Qt in" << absoluteQtPath; |
|
534 QString finalQtPath = absoluteQtPath; |
|
535 |
|
536 if (!absoluteQtPath.startsWith("/Library/Frameworks")) |
|
537 finalQtPath += "/lib/"; |
|
538 |
|
539 foreach (FrameworkInfo framework, frameworks) { |
|
540 const QString oldBinaryId = framework.installName; |
|
541 const QString newBinaryId = finalQtPath + framework.frameworkName + framework.binaryPath; |
|
542 changeInstallName(oldBinaryId, newBinaryId, appBinaryPath); |
|
543 } |
|
544 } |
|
545 |
|
546 void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs) |
|
547 { |
|
548 const QString appBinaryPath = findAppBinary(appPath); |
|
549 const QList<FrameworkInfo> frameworks = getQtFrameworks(appBinaryPath, useDebugLibs); |
|
550 if (frameworks.isEmpty()) { |
|
551 LogWarning(); |
|
552 LogWarning() << "Could not find any _external_ Qt frameworks to change in" << appPath; |
|
553 return; |
|
554 } else { |
|
555 const QString absoluteQtPath = QDir(qtPath).absolutePath(); |
|
556 changeQtFrameworks(frameworks, appBinaryPath, absoluteQtPath); |
|
557 } |
|
558 } |
|
559 |
|
560 |
|
561 void createDiskImage(const QString &appBundlePath) |
|
562 { |
|
563 QString appBaseName = appBundlePath; |
|
564 appBaseName.chop(4); // remove ".app" from end |
|
565 |
|
566 QString dmgName = appBaseName + ".dmg"; |
|
567 |
|
568 QFile dmg(dmgName); |
|
569 |
|
570 if (dmg.exists()) { |
|
571 LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); |
|
572 } else { |
|
573 LogNormal() << "Creating disk image (.dmg) for" << appBundlePath; |
|
574 } |
|
575 |
|
576 // More dmg options can be found in the hdiutil man page. |
|
577 QString options = QString("create %1.dmg -srcfolder %1.app -format UDZO -volname %1").arg(appBaseName); |
|
578 |
|
579 QProcess hdutil; |
|
580 hdutil.start("hdiutil", options.split(' ')); |
|
581 hdutil.waitForFinished(-1); |
|
582 } |