diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 10:34:13 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 10:34:13 +0100 |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /tools/macdeployqt | |
download | qt4-tools-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz |
Long live Qt!
Diffstat (limited to 'tools/macdeployqt')
-rw-r--r-- | tools/macdeployqt/macchangeqt/macchangeqt.pro | 9 | ||||
-rw-r--r-- | tools/macdeployqt/macchangeqt/main.cpp | 76 | ||||
-rw-r--r-- | tools/macdeployqt/macdeployqt.pro | 7 | ||||
-rw-r--r-- | tools/macdeployqt/macdeployqt/macdeployqt.pro | 13 | ||||
-rw-r--r-- | tools/macdeployqt/macdeployqt/main.cpp | 135 | ||||
-rw-r--r-- | tools/macdeployqt/shared/shared.cpp | 582 | ||||
-rw-r--r-- | tools/macdeployqt/shared/shared.h | 110 | ||||
-rw-r--r-- | tools/macdeployqt/tests/deployment_mac.pro | 10 | ||||
-rw-r--r-- | tools/macdeployqt/tests/tst_deployment_mac.cpp | 233 |
9 files changed, 1175 insertions, 0 deletions
diff --git a/tools/macdeployqt/macchangeqt/macchangeqt.pro b/tools/macdeployqt/macchangeqt/macchangeqt.pro new file mode 100644 index 0000000000..c09fea3be1 --- /dev/null +++ b/tools/macdeployqt/macchangeqt/macchangeqt.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = macchangeqt +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp ../shared/shared.cpp +CONFIG += qt warn_on +CONFIG -= app_bundle diff --git a/tools/macdeployqt/macchangeqt/main.cpp b/tools/macdeployqt/macchangeqt/main.cpp new file mode 100644 index 0000000000..ebdfc14d18 --- /dev/null +++ b/tools/macdeployqt/macchangeqt/main.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "../shared/shared.h" + +int main(int argc, char **argv) +{ + // useDebugLibs should always be false because even if set all Qt + // libraries inside a binary to point to debug versions, as soon as + // one of them loads a Qt plugin, the plugin itself will load the + // release version of Qt, and as such, the app will crash. + bool useDebugLibs = false; + + int optionsSpecified = 0; + for (int i = 2; i < argc; ++i) { + QByteArray argument = QByteArray(argv[i]); + if (argument.startsWith(QByteArray("-verbose="))) { + LogDebug() << "Argument found:" << argument; + optionsSpecified++; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; + } + } + + if (argc != (3 + optionsSpecified)) { + qDebug() << "Changeqt: changes witch Qt frameworks an application links against."; + qDebug() << "Usage: changeqt app-bundle qt-dir <-verbose=[0-3]>"; + return 0; + } + + const QString appPath = QString::fromLocal8Bit(argv[1]); + const QString qtPath = QString::fromLocal8Bit(argv[2]); + changeQtFrameworks(appPath, qtPath, useDebugLibs); +} diff --git a/tools/macdeployqt/macdeployqt.pro b/tools/macdeployqt/macdeployqt.pro new file mode 100644 index 0000000000..09ed6989aa --- /dev/null +++ b/tools/macdeployqt/macdeployqt.pro @@ -0,0 +1,7 @@ + +mac { + +TEMPLATE = subdirs +SUBDIRS = macdeployqt macchangeqt + +} diff --git a/tools/macdeployqt/macdeployqt/macdeployqt.pro b/tools/macdeployqt/macdeployqt/macdeployqt.pro new file mode 100644 index 0000000000..3e56024fde --- /dev/null +++ b/tools/macdeployqt/macdeployqt/macdeployqt.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = macdeployqt +DEPENDPATH += . +INCLUDEPATH += . +DESTDIR = ../../../bin + +# Input +SOURCES += main.cpp ../shared/shared.cpp +CONFIG += qt warn_on +CONFIG -= app_bundle + +target.path=$$[QT_INSTALL_BINS] +INSTALLS += target diff --git a/tools/macdeployqt/macdeployqt/main.cpp b/tools/macdeployqt/macdeployqt/main.cpp new file mode 100644 index 0000000000..5836cd0eef --- /dev/null +++ b/tools/macdeployqt/macdeployqt/main.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "../shared/shared.h" +#include <qdir.h> + +int main(int argc, char **argv) +{ + QString appBundlePath; + if (argc > 1) + appBundlePath = QString::fromLocal8Bit(argv[1]); + + if (argc < 2 || appBundlePath.startsWith("-")) { + qDebug() << "Usage: macdeployqt app-bundle [options]"; + qDebug() << ""; + qDebug() << "Options:"; + qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug"; + qDebug() << " -no-plugins : Skip plugin deployment"; + qDebug() << " -dmg : Create a .dmg disk image"; + qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)"; + qDebug() << ""; + qDebug() << "macdeployqt takes an application bundle as input and makes it"; + qDebug() << "self-contained by copying in the Qt frameworks and plugins that"; + qDebug() << "the application uses."; + qDebug() << ""; + qDebug() << "Plugins related to a framework are copied in with the"; + qDebug() << "framework. The accessibilty, image formats, and text codec"; + qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; + qDebug() << ""; + qDebug() << "See the \"Deploying an Application on Qt/Mac\" typic in the"; + qDebug() << "documentation for more information about deployment on Mac OS X."; + + return 0; + } + + if (appBundlePath.endsWith("/")) + appBundlePath.chop(1); + + if (QDir().exists(appBundlePath) == false) { + qDebug() << "Error: Could not find app bundle" << appBundlePath; + return 0; + } + + bool plugins = true; + bool dmg = false; + bool useDebugLibs = false; + extern bool runStripEnabled; + + for (int i = 2; i < argc; ++i) { + QByteArray argument = QByteArray(argv[i]); + if (argument == QByteArray("-no-plugins")) { + LogDebug() << "Argument found:" << argument; + plugins = false; + } else if (argument == QByteArray("-dmg")) { + LogDebug() << "Argument found:" << argument; + dmg = true; + } else if (argument == QByteArray("-no-strip")) { + LogDebug() << "Argument found:" << argument; + runStripEnabled = false; + } else if (argument == QByteArray("-use-debug-libs")) { + LogDebug() << "Argument found:" << argument; + useDebugLibs = true; + runStripEnabled = false; + } else if (argument.startsWith(QByteArray("-verbose"))) { + LogDebug() << "Argument found:" << argument; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; + } else if (argument.startsWith("-")) { + LogError() << "Unknown argument" << argument << "\n"; + return 0; + } + } + + DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, useDebugLibs); + + if (plugins) { + if (deploymentInfo.qtPath.isEmpty()) + deploymentInfo.pluginPath = "/Developer/Applications/Qt/plugins"; // Assume binary package. + else + deploymentInfo.pluginPath = deploymentInfo.qtPath + "/plugins"; + + LogNormal(); + deployPlugins(appBundlePath, deploymentInfo, useDebugLibs); + createQtConf(appBundlePath); + } + + if (dmg) { + LogNormal(); + createDiskImage(appBundlePath); + } +} + diff --git a/tools/macdeployqt/shared/shared.cpp b/tools/macdeployqt/shared/shared.cpp new file mode 100644 index 0000000000..a10e6685d5 --- /dev/null +++ b/tools/macdeployqt/shared/shared.cpp @@ -0,0 +1,582 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QString> +#include <QStringList> +#include <QDebug> +#include <iostream> +#include <QProcess> +#include <QDir> +#include <QRegExp> +#include <QSet> +#include <QDirIterator> +#include "shared.h" + +bool runStripEnabled = true; +int logLevel = 1; + +using std::cout; +using std::endl; + +bool operator==(const FrameworkInfo &a, const FrameworkInfo &b) +{ + return ((a.frameworkPath == b.frameworkPath) && (a.binaryPath == b.binaryPath)); +} + +QDebug operator<<(QDebug debug, const FrameworkInfo &info) +{ + debug << "Framework name" << info.frameworkName << "\n"; + debug << "Framework directory" << info.frameworkDirectory << "\n"; + debug << "Framework path" << info.frameworkPath << "\n"; + debug << "Binary directory" << info.binaryDirectory << "\n"; + debug << "Binary name" << info.binaryName << "\n"; + debug << "Binary path" << info.binaryPath << "\n"; + debug << "Version" << info.version << "\n"; + debug << "Install name" << info.installName << "\n"; + debug << "Deployed install name" << info.deployedInstallName << "\n"; + debug << "Source file Path" << info.sourceFilePath << "\n"; + debug << "Deployed Directory (relative to bundle)" << info.destinationDirectory << "\n"; + + return debug; +} + +const QString bundleFrameworkDirectory = "Contents/Frameworks"; +const QString bundleBinaryDirectory = "Contents/MacOS"; + +inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info) +{ + debug << "Application bundle path" << info.path << "\n"; + debug << "Binary path" << info.binaryPath << "\n"; + return debug; +} + +bool copyFilePrintStatus(const QString &from, const QString &to) +{ + if (QFile::copy(from, to)) { + LogNormal() << " copied:" << from; + LogNormal() << " to" << to; + return true; + } else { + LogError() << "file copy failed from" << from; + LogError() << " to" << to; + return false; + } +} + +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs) +{ + FrameworkInfo info; + QString trimmed = line.trimmed(); + + if (trimmed.isEmpty()) + return info; + + // Don't deploy system libraries. + if (trimmed.startsWith("/System/Library/") || + (trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene + || trimmed.startsWith("@executable_path")) + return info; + + enum State {QtPath, FrameworkName, DylibName, Version, End}; + State state = QtPath; + int part = 0; + QString name; + QString qtPath; + QString suffix = useDebugLibs ? "_debug" : ""; + + // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/ + QStringList parts = trimmed.split("/"); + while (part < parts.count()) { + const QString currentPart = parts.at(part).simplified() ; + ++part; + if (currentPart == "") + continue; + + if (state == QtPath) { + // Check for library name part + if (part < parts.count() && parts.at(part).contains(".dylib ")) { + state = DylibName; + info.installName += "/" + (qtPath + "lib/").simplified(); + info.frameworkDirectory = info.installName; + state = DylibName; + continue; + } else if (part < parts.count() && parts.at(part).endsWith(".framework")) { + info.installName += "/" + (qtPath + "lib/").simplified(); + info.frameworkDirectory = info.installName; + state = FrameworkName; + continue; + } else if (trimmed.startsWith("/") == false) { // If the line does not contain a full path, the app is using a binary Qt package. + if (currentPart.contains(".framework")) { + info.frameworkDirectory = "/Library/Frameworks/"; + state = FrameworkName; + } else { + info.frameworkDirectory = "/usr/lib/"; + state = DylibName; + } + + --part; + continue; + } + qtPath += (currentPart + "/"); + + } if (state == FrameworkName) { + // remove ".framework" + name = currentPart; + name.chop(QString(".framework").length()); + info.frameworkName = currentPart; + state = Version; + ++part; + continue; + } if (state == DylibName) { + name = currentPart.split(" (compatibility").at(0); + info.frameworkName = name; + info.binaryName = name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.')); + info.installName += name; + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName; + info.frameworkPath = info.frameworkDirectory + info.binaryName; + info.sourceFilePath = info.frameworkPath; + info.destinationDirectory = bundleFrameworkDirectory + "/"; + info.binaryDirectory = info.frameworkDirectory; + info.binaryPath = info.frameworkPath; + state = End; + ++part; + continue; + } else if (state == Version) { + info.version = currentPart; + info.binaryDirectory = "Versions/" + info.version; + info.binaryName = name + suffix; + info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName; + info.installName += info.frameworkName + "/" + info.binaryDirectory + "/" + name; + info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath; + info.frameworkPath = info.frameworkDirectory + info.frameworkName; + info.sourceFilePath = info.frameworkPath + info.binaryPath; + info.destinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName + "/" + info.binaryDirectory; + state = End; + } else if (state == End) { + break; + } + } + + return info; +} + +QString findAppBinary(const QString &appBundlePath) +{ + QString appName = QFileInfo(appBundlePath).completeBaseName(); + QString binaryPath = appBundlePath + "/Contents/MacOS/" + appName; + + if (QFile::exists(binaryPath)) + return binaryPath; + LogError() << "Could not find bundle binary for" << appBundlePath; + return QString(); +} + +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs) +{ + QList<FrameworkInfo> libraries; + foreach(const QString line, otoolLines) { + FrameworkInfo info = parseOtoolLibraryLine(line, useDebugLibs); + if (info.frameworkName.isEmpty() == false) { + LogDebug() << "Adding framework:"; + LogDebug() << info; + libraries.append(info); + } + } + return libraries; +} + +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs) +{ + LogDebug() << "Using otool:"; + LogDebug() << " inspecting" << path; + QProcess otool; + otool.start("otool", QStringList() << "-L" << path); + otool.waitForFinished(); + + if (otool.exitCode() != 0) { + LogError() << otool.readAllStandardError(); + } + + QString output = otool.readAllStandardOutput(); + QStringList outputLines = output.split("\n"); + outputLines.removeFirst(); // remove line containing the binary path + if (path.contains(".framework") || path.contains(".dylib")) + outputLines.removeFirst(); // frameworks and dylibs lists themselves as a dependency. + + return getQtFrameworks(outputLines, useDebugLibs); +} + +// copies everything _inside_ sourcePath to destinationPath +void recursiveCopy(const QString &sourcePath, const QString &destinationPath) +{ + QDir().mkpath(destinationPath); + + QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot); + foreach (QString file, files) { + const QString fileSourcePath = sourcePath + "/" + file; + const QString fileDestinationPath = destinationPath + "/" + file; + copyFilePrintStatus(fileSourcePath, fileDestinationPath); + } + + QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); + foreach (QString dir, subdirs) { + recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir); + } +} + +QString copyFramework(const FrameworkInfo &framework, const QString path) +{ + QString from = framework.sourceFilePath; + QString toDir = path + "/" + framework.destinationDirectory; + QString to = toDir + "/" + framework.binaryName; + + if (QFile::exists(from) == false) { + LogError() << "no file at" << from; + return QString(); + } + + + QDir dir; + if (dir.mkpath(toDir) == false) { + LogError() << "could not create destination directory" << to; + return QString(); + } + + + if (QFile::exists(to)) { + return QString(); + } + + copyFilePrintStatus(from, to); + + const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; + const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; + recursiveCopy(resourcesSourcePath, resourcesDestianationPath); + + return to; +} + +void runInstallNameTool(QStringList options) +{ + QProcess installNametool; + installNametool.start("install_name_tool", options); + installNametool.waitForFinished(); + if (installNametool.exitCode() != 0) { + LogError() << installNametool.readAllStandardError(); + LogError() << installNametool.readAllStandardOutput(); + } +} + +void changeIdentification(const QString &id, const QString &binaryPath) +{ + LogDebug() << "Using install_name_tool:"; + LogDebug() << " change identification in" << binaryPath; + LogDebug() << " to" << id; + runInstallNameTool(QStringList() << "-id" << id << binaryPath); +} + +void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath) +{ + LogDebug() << "Using install_name_tool:"; + LogDebug() << " in" << binaryPath; + LogDebug() << " change reference" << oldName; + LogDebug() << " to" << newName; + runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath); +} + +void runStrip(const QString &binaryPath) +{ + if (runStripEnabled == false) + return; + + LogDebug() << "Using strip:"; + LogDebug() << " stripped" << binaryPath; + QProcess strip; + strip.start("strip", QStringList() << "-x" << binaryPath); + strip.waitForFinished(); + if (strip.exitCode() != 0) { + LogError() << strip.readAllStandardError(); + LogError() << strip.readAllStandardOutput(); + } +} + +/* + Deploys the the listed frameworks listed into an app bundle. + The frameworks are searched for dependencies, which are also deployed. + (deploying Qt3Support will also deploy QtNetwork and QtSql for example.) + Returns a DeploymentInfo structure containing the Qt path used and a + a list of actually deployed frameworks. +*/ +DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, + const QString &bundlePath, const QString &binaryPath, bool useDebugLibs) +{ + LogNormal(); + LogNormal() << "Deploying Qt frameworks found inside:" << binaryPath; + QStringList copiedFrameworks; + DeploymentInfo deploymenInfo; + + while (frameworks.isEmpty() == false) { + const FrameworkInfo framework = frameworks.takeFirst(); + copiedFrameworks.append(framework.frameworkName); + + // Get the qt path from one of the Qt frameworks; + if (deploymenInfo.qtPath == QString() && framework.frameworkName.contains("Qt") + && framework.frameworkDirectory.contains("/lib")) + { + deploymenInfo.qtPath = framework.frameworkDirectory; + deploymenInfo.qtPath.chop(5); // remove "/lib/" + } + + if (framework.installName.startsWith("/@executable_path/")) { + LogError() << framework.frameworkName << "already deployed, skipping."; + continue; + } + + // Install_name_tool the new id into the binary + changeInstallName(framework.installName, framework.deployedInstallName, binaryPath); + + // Copy farmework to app bundle. + const QString deployedBinaryPath = copyFramework(framework, bundlePath); + // Skip the rest if already was deployed. + if (deployedBinaryPath == QString()) + continue; + + runStrip(deployedBinaryPath); + + // Install_name_tool it a new id. + changeIdentification(framework.deployedInstallName, deployedBinaryPath); + // Check for framework dependencies + QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs); + + foreach (FrameworkInfo dependency, dependencies) { + changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath); + + // Deploy framework if neccesary. + if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) { + frameworks.append(dependency); + } + } + } + deploymenInfo.deployedFrameworks = copiedFrameworks; + return deploymenInfo; +} + +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs) +{ + ApplicationBundleInfo applicationBundle; + applicationBundle.path = appBundlePath; + applicationBundle.binaryPath = findAppBinary(appBundlePath); + QList<FrameworkInfo> frameworks = getQtFrameworks(applicationBundle.binaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath; + LogWarning() << "Perhaps macdeployqt was already used on" << appBundlePath << "?"; + LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again."; + return DeploymentInfo(); + } else { + return deployQtFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, useDebugLibs); + } +} + +void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, + const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs) +{ + LogNormal() << "Deploying plugins from" << pluginSourcePath; + QStringList plugins = QDir(pluginSourcePath).entryList(QStringList() << "*.dylib"); + + foreach (QString pluginName, plugins) { + if (pluginSourcePath.contains(deploymentInfo.pluginPath)) { + QStringList deployedFrameworks = deploymentInfo.deployedFrameworks; + + // Skip the debug versions of the plugins, unless specified otherwise. + if (!useDebugLibs && pluginName.endsWith("_debug.dylib")) + continue; + + // Skip the release versions of the plugins, unless specified otherwise. + if (useDebugLibs && !pluginName.endsWith("_debug.dylib")) + continue; + + // Skip the designer plugins + if (pluginSourcePath.contains("plugins/designer")) + continue; + +#ifndef QT_GRAPHICSSYSTEM_OPENGL + // SKip the opengl graphicssystem plugin when not in use. + if (pluginName.contains("libqglgraphicssystem")) + continue; +#endif + // Deploy accessibility for Qt3Support only if the Qt3Support.framework is in use + if (deployedFrameworks.indexOf("Qt3Support.framework") == -1 && pluginName.contains("accessiblecompatwidgets")) + continue; + + // Deploy the svg icon plugin if QtSvg.framework is in use. + if (deployedFrameworks.indexOf("QtSvg.framework") == -1 && pluginName.contains("svg")) + continue; + + // Deploy the phonon plugins if phonon.framework is in use + if (deployedFrameworks.indexOf("phonon.framework") == -1 && pluginName.contains("phonon")) + continue; + + // Deploy the sql plugins if QtSql.framework is in use + if (deployedFrameworks.indexOf("QtSql.framework") == -1 && pluginName.contains("sql")) + continue; + + // Deploy the script plugins if QtScript.framework is in use + if (deployedFrameworks.indexOf("QtScript.framework") == -1 && pluginName.contains("script")) + continue; + } + + QDir dir; + dir.mkpath(pluginDestinationPath); + + const QString sourcePath = pluginSourcePath + "/" + pluginName; + const QString destinationPath = pluginDestinationPath + "/" + pluginName; + if (copyFilePrintStatus(sourcePath, destinationPath)) { + + runStrip(destinationPath); + + // Special case for the phonon plugin: CoreVideo is not available as a separate framework + // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) + if (pluginName.contains("libphonon_qt7")) { + changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", + "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", + destinationPath); + } + + QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs); + deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath, useDebugLibs); + } + } // foreach plugins + + QStringList subdirs = QDir(pluginSourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &subdir, subdirs) + deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo, useDebugLibs); +} + +void createQtConf(const QString &appBundlePath) +{ + QByteArray contents = "[Paths]\nPlugins = PlugIns\n"; + QString filePath = appBundlePath + "/Contents/Resources/"; + QString fileName = filePath + "qt.conf"; + + QDir().mkpath(filePath); + + QFile qtconf(fileName); + if (qtconf.exists()) { + LogWarning(); + LogWarning() << fileName << "already exists, will not overwrite."; + LogWarning() << "To make sure the plugins are loaded from the correct location,"; + LogWarning() << "please make sure qt.conf contains the following lines:"; + LogWarning() << "[Paths]"; + LogWarning() << " Plugins = PlugIns"; + return; + } + + qtconf.open(QIODevice::WriteOnly); + if (qtconf.write(contents) != -1) { + LogNormal() << "Created configuration file:" << fileName; + LogNormal() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; + } +} + +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs) +{ + ApplicationBundleInfo applicationBundle; + applicationBundle.path = appBundlePath; + applicationBundle.binaryPath = findAppBinary(appBundlePath); + + const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns"; + deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs); +} + + +void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &absoluteQtPath) +{ + LogNormal() << "Changing" << appBinaryPath << "to link against"; + LogNormal() << "Qt in" << absoluteQtPath; + QString finalQtPath = absoluteQtPath; + + if (!absoluteQtPath.startsWith("/Library/Frameworks")) + finalQtPath += "/lib/"; + + foreach (FrameworkInfo framework, frameworks) { + const QString oldBinaryId = framework.installName; + const QString newBinaryId = finalQtPath + framework.frameworkName + framework.binaryPath; + changeInstallName(oldBinaryId, newBinaryId, appBinaryPath); + } +} + +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs) +{ + const QString appBinaryPath = findAppBinary(appPath); + const QList<FrameworkInfo> frameworks = getQtFrameworks(appBinaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any _external_ Qt frameworks to change in" << appPath; + return; + } else { + const QString absoluteQtPath = QDir(qtPath).absolutePath(); + changeQtFrameworks(frameworks, appBinaryPath, absoluteQtPath); + } +} + + +void createDiskImage(const QString &appBundlePath) +{ + QString appBaseName = appBundlePath; + appBaseName.chop(4); // remove ".app" from end + + QString dmgName = appBaseName + ".dmg"; + + QFile dmg(dmgName); + + if (dmg.exists()) { + LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); + } else { + LogNormal() << "Creating disk image (.dmg) for" << appBundlePath; + } + + // More dmg options can be found in the hdiutil man page. + QString options = QString("create %1.dmg -srcfolder %1.app -format UDZO -volname %1").arg(appBaseName); + + QProcess hdutil; + hdutil.start("hdiutil", options.split(' ')); + hdutil.waitForFinished(-1); +} diff --git a/tools/macdeployqt/shared/shared.h b/tools/macdeployqt/shared/shared.h new file mode 100644 index 0000000000..637873e42d --- /dev/null +++ b/tools/macdeployqt/shared/shared.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAC_DEPLOMYMENT_SHARED_H +#define MAC_DEPLOMYMENT_SHARED_H + +#include <QString> +#include <QStringList> +#include <QDebug> + +extern int logLevel; +#define LogError() if (logLevel < 1) {} else qDebug() << "ERROR:" +#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:" +#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:" +#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:" + +extern bool runStripEnabled; + +class FrameworkInfo +{ +public: + QString frameworkDirectory; + QString frameworkName; + QString frameworkPath; + QString binaryDirectory; + QString binaryName; + QString binaryPath; + QString version; + QString installName; + QString deployedInstallName; + QString sourceFilePath; + QString destinationDirectory; +}; + +bool operator==(const FrameworkInfo &a, const FrameworkInfo &b); +QDebug operator<<(QDebug debug, const FrameworkInfo &info); + +class ApplicationBundleInfo +{ + public: + QString path; + QString binaryPath; +}; + +class DeploymentInfo +{ +public: + QString qtPath; + QString pluginPath; + QStringList deployedFrameworks; +}; + + +inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info); + +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs); +void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &qtPath); + +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs); +QString findAppBinary(const QString &appBundlePath); +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs); +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs); +QString copyFramework(const FrameworkInfo &framework, const QString path); +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs); +DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString &bundlePath, const QString &binaryPath); +void createQtConf(const QString &appBundlePath); +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs); +void changeIdentification(const QString &id, const QString &binaryPath); +void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath); +QString findAppBinary(const QString &appBundlePath); +void createDiskImage(const QString &appBundlePath); + +#endif diff --git a/tools/macdeployqt/tests/deployment_mac.pro b/tools/macdeployqt/tests/deployment_mac.pro new file mode 100644 index 0000000000..1bde1fdc5c --- /dev/null +++ b/tools/macdeployqt/tests/deployment_mac.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +DEPENDPATH += . ../shared/ +INCLUDEPATH += . ../shared/ +TARGET=tst_deployment_mac +CONFIG += qtestlib + +# Input +SOURCES += tst_deployment_mac.cpp ../shared/shared.cpp +HEADERS += ../shared/shared.h + diff --git a/tools/macdeployqt/tests/tst_deployment_mac.cpp b/tools/macdeployqt/tests/tst_deployment_mac.cpp new file mode 100644 index 0000000000..5f9c99082d --- /dev/null +++ b/tools/macdeployqt/tests/tst_deployment_mac.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore> +#include <QtTest/QTest> +#include <shared.h> + +class tst_deployment_mac : public QObject +{ +Q_OBJECT +private slots: + void testParseOtoolLibraryLine(); + void testgetQtFrameworks(); + void testFindAppBinarty(); +}; + +void tst_deployment_mac::testParseOtoolLibraryLine() +{ +{ + QString line = " /Users/foo/build/qt-4.4/lib/QtGui.framework/Versions/4/QtGui (compatibility version 4.4.0, current version 4.4.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/Users/foo/build/qt-4.4/lib/")); + QCOMPARE(info.frameworkName, QLatin1String("QtGui.framework")); + QCOMPARE(info.frameworkPath, QLatin1String("/Users/foo/build/qt-4.4/lib/QtGui.framework")); + QCOMPARE(info.binaryDirectory, QLatin1String("Versions/4")); + QCOMPARE(info.binaryName, QLatin1String("QtGui")); + QCOMPARE(info.binaryPath, QLatin1String("/Versions/4/QtGui")); + QCOMPARE(info.version, QLatin1String("4")); + QCOMPARE(info.installName, QLatin1String("/Users/foo/build/qt-4.4/lib/QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.sourceFilePath, QLatin1String("/Users/foo/build/qt-4.4/lib/QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/QtGui.framework/Versions/4")); +} +{ + QString line = " /Users/foo/build/qt-4.4/lib/phonon.framework/Versions/4/phonon (compatibility version 4.1.0, current version 4.1.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/Users/foo/build/qt-4.4/lib/")); + QCOMPARE(info.frameworkName, QLatin1String("phonon.framework")); + QCOMPARE(info.frameworkPath, QLatin1String("/Users/foo/build/qt-4.4/lib/phonon.framework")); + QCOMPARE(info.binaryDirectory, QLatin1String("Versions/4")); + QCOMPARE(info.binaryName, QLatin1String("phonon")); + QCOMPARE(info.binaryPath, QLatin1String("/Versions/4/phonon")); + QCOMPARE(info.version, QLatin1String("4")); + QCOMPARE(info.installName, QLatin1String("/Users/foo/build/qt-4.4/lib/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.sourceFilePath, QLatin1String("/Users/foo/build/qt-4.4/lib/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/phonon.framework/Versions/4")); +} + +{ + QString line = " /usr/local/Trolltech/Qt-4.4.0/lib/phonon.framework/Versions/4/phonon (compatibility version 4.1.0, current version 4.1.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/usr/local/Trolltech/Qt-4.4.0/lib/")); + QCOMPARE(info.frameworkName, QLatin1String("phonon.framework")); + QCOMPARE(info.frameworkPath, QLatin1String("/usr/local/Trolltech/Qt-4.4.0/lib/phonon.framework")); + QCOMPARE(info.binaryDirectory, QLatin1String("Versions/4")); + QCOMPARE(info.binaryName, QLatin1String("phonon")); + QCOMPARE(info.binaryPath, QLatin1String("/Versions/4/phonon")); + QCOMPARE(info.version, QLatin1String("4")); + QCOMPARE(info.installName, QLatin1String("/usr/local/Trolltech/Qt-4.4.0/lib/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.sourceFilePath, QLatin1String("/usr/local/Trolltech/Qt-4.4.0/lib/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/phonon.framework/Versions/4")); +} + +{ + QString line = " QtGui.framework/Versions/4/QtGui (compatibility version 4.1.0, current version 4.1.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/Library/Frameworks/")); + QCOMPARE(info.frameworkName, QLatin1String("QtGui.framework")); + QCOMPARE(info.frameworkPath, QLatin1String("/Library/Frameworks/QtGui.framework")); + QCOMPARE(info.binaryDirectory, QLatin1String("Versions/4")); + QCOMPARE(info.binaryName, QLatin1String("QtGui")); + QCOMPARE(info.binaryPath, QLatin1String("/Versions/4/QtGui")); + QCOMPARE(info.version, QLatin1String("4")); + QCOMPARE(info.installName, QLatin1String("QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.sourceFilePath, QLatin1String("/Library/Frameworks/QtGui.framework/Versions/4/QtGui")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/QtGui.framework/Versions/4")); +} + +{ + QString line = " phonon.framework/Versions/4/QtGui (compatibility version 4.1.0, current version 4.1.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/Library/Frameworks/")); + QCOMPARE(info.frameworkName, QLatin1String("phonon.framework")); + QCOMPARE(info.frameworkPath, QLatin1String("/Library/Frameworks/phonon.framework")); + QCOMPARE(info.binaryDirectory, QLatin1String("Versions/4")); + QCOMPARE(info.binaryName, QLatin1String("phonon")); + QCOMPARE(info.binaryPath, QLatin1String("/Versions/4/phonon")); + QCOMPARE(info.version, QLatin1String("4")); + QCOMPARE(info.installName, QLatin1String("phonon.framework/Versions/4/phonon")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.sourceFilePath, QLatin1String("/Library/Frameworks/phonon.framework/Versions/4/phonon")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/phonon.framework/Versions/4")); +} + +{ + QString line = " /Users/foo/build/qt-4.4/lib/libQtCLucene.4.dylib (compatibility version 4.4.0, current version 4.4.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/Users/foo/build/qt-4.4/lib/")); + QCOMPARE(info.binaryName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(info.frameworkName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(info.frameworkPath, QLatin1String("/Users/foo/build/qt-4.4/lib/libQtCLucene.4.dylib")); + QCOMPARE(info.installName, QLatin1String("/Users/foo/build/qt-4.4/lib/libQtCLucene.4.dylib")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/libQtCLucene.4.dylib")); + QCOMPARE(info.sourceFilePath, QLatin1String("/Users/foo/build/qt-4.4/lib/libQtCLucene.4.dylib")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/")); +} +{ + QString line = "libQtCLucene.4.dylib (compatibility version 4.4.0, current version 4.4.0)"; + FrameworkInfo info = parseOtoolLibraryLine(line); +// qDebug() << info; + QCOMPARE(info.frameworkDirectory, QLatin1String("/usr/lib/")); + QCOMPARE(info.binaryName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(info.frameworkName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(info.frameworkPath, QLatin1String("/usr/lib/libQtCLucene.4.dylib")); + QCOMPARE(info.installName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(info.deployedInstallName, QLatin1String("@executable_path/../Frameworks/libQtCLucene.4.dylib")); + QCOMPARE(info.sourceFilePath, QLatin1String("/usr/lib/libQtCLucene.4.dylib")); + QCOMPARE(info.destinationDirectory, QLatin1String("Contents/Frameworks/")); +} +{ + QString line = "/foo"; //invalid + FrameworkInfo info = parseOtoolLibraryLine(line); + QCOMPARE(info.frameworkName, QString()); +} + +} + +void tst_deployment_mac::testgetQtFrameworks() +{ +{ + QStringList otool = QStringList() + << "/Users/foo/build/qt-4.4/lib/phonon.framework/Versions/4/phonon (compatibility version 4.1.0, current version 4.1.0)" + << "/Users/foo/build/qt-4.4/lib/QtGui.framework/Versions/4/QtGui (compatibility version 4.4.0, current version 4.4.0)" + << "/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 136.0.0)" + << "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.27.0)" + << "/Users/foo/build/qt-4.4/lib/QtCore.framework/Versions/4/QtCore (compatibility version 4.4.0, current version 4.4.0)" + << "/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3)" + << "/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)" + << "/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.4.0)" + << "/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)" + << " " + ; + + QList<FrameworkInfo> frameworks = getQtFrameworks(otool); + QCOMPARE(frameworks.count(), 3); + QCOMPARE(frameworks.at(0).binaryName, QLatin1String("phonon")); + QCOMPARE(frameworks.at(1).binaryName, QLatin1String("QtGui")); + QCOMPARE(frameworks.at(2).binaryName, QLatin1String("QtCore")); +} +{ + QStringList otool = QStringList() + << "QtHelp.framework/Versions/4/QtHelp (compatibility version 4.4.0, current version 4.4.0)" + << "libQtCLucene.4.dylib (compatibility version 4.4.0, current version 4.4.0)" + << "QtSql.framework/Versions/4/QtSql (compatibility version 4.4.0, current version 4.4.0)" + << "QtXml.framework/Versions/4/QtXml (compatibility version 4.4.0, current version 4.4.0)" + << "QtGui.framework/Versions/4/QtGui (compatibility version 4.4.0, current version 4.4.0)" + << "/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 128.0.0)" + << "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 824.42.0)" + << "QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.4.0, current version 4.4.0)" + << "QtCore.framework/Versions/4/QtCore (compatibility version 4.4.0, current version 4.4.0)" + << "/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3)" + << "/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.6)" + << "/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.4.0)" + << "/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)" + ; + + QList<FrameworkInfo> frameworks = getQtFrameworks(otool); + QCOMPARE(frameworks.count(), 7); + QCOMPARE(frameworks.at(0).binaryName, QLatin1String("QtHelp")); + QCOMPARE(frameworks.at(1).binaryName, QLatin1String("libQtCLucene.4.dylib")); + QCOMPARE(frameworks.at(2).binaryName, QLatin1String("QtSql")); + QCOMPARE(frameworks.at(3).binaryName, QLatin1String("QtXml")); + QCOMPARE(frameworks.at(4).binaryName, QLatin1String("QtGui")); + QCOMPARE(frameworks.at(5).binaryName, QLatin1String("QtNetwork")); + QCOMPARE(frameworks.at(6).binaryName, QLatin1String("QtCore")); +} + +} + +void tst_deployment_mac::testFindAppBinarty() +{ + QCOMPARE(findAppBinary("tst_deployment_mac.app"), QLatin1String("tst_deployment_mac.app/Contents/MacOS/tst_deployment_mac")); +} + +QTEST_MAIN(tst_deployment_mac) + +#include "tst_deployment_mac.moc"
\ No newline at end of file |