summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/assistant/help/qhelpcontentwidget.cpp47
-rw-r--r--src/assistant/help/qhelpenginecore.cpp1
-rw-r--r--src/assistant/help/qhelpenginecore.h1
-rw-r--r--src/assistant/help/qhelpindexwidget.cpp2
-rw-r--r--src/macdeployqt/macdeployqt/main.cpp15
-rw-r--r--src/macdeployqt/shared/shared.cpp320
-rw-r--r--src/macdeployqt/shared/shared.h6
-rw-r--r--src/qconfig/main.cpp2
8 files changed, 331 insertions, 63 deletions
diff --git a/src/assistant/help/qhelpcontentwidget.cpp b/src/assistant/help/qhelpcontentwidget.cpp
index aa06e29d8..8f38bcfd3 100644
--- a/src/assistant/help/qhelpcontentwidget.cpp
+++ b/src/assistant/help/qhelpcontentwidget.cpp
@@ -65,6 +65,7 @@ public:
class QHelpContentProvider : public QThread
{
+ Q_OBJECT
public:
QHelpContentProvider(QHelpEnginePrivate *helpEngine);
~QHelpContentProvider();
@@ -73,11 +74,13 @@ public:
QHelpContentItem *rootItem();
int nextChildCount() const;
+signals:
+ void finishedSuccessFully();
+
private:
void run();
QHelpEnginePrivate *m_helpEngine;
- QHelpContentItem *m_rootItem;
QStringList m_filterAttributes;
QQueue<QHelpContentItem*> m_rootItems;
QMutex m_mutex;
@@ -188,7 +191,6 @@ QHelpContentProvider::QHelpContentProvider(QHelpEnginePrivate *helpEngine)
: QThread(helpEngine)
{
m_helpEngine = helpEngine;
- m_rootItem = 0;
m_abort = false;
}
@@ -212,22 +214,28 @@ void QHelpContentProvider::collectContents(const QString &customFilterName)
void QHelpContentProvider::stopCollecting()
{
- if (!isRunning())
- return;
- m_mutex.lock();
- m_abort = true;
- m_mutex.unlock();
- wait();
+ if (isRunning()) {
+ m_mutex.lock();
+ m_abort = true;
+ m_mutex.unlock();
+ wait();
+ }
+ qDeleteAll(m_rootItems);
+ m_rootItems.clear();
}
QHelpContentItem *QHelpContentProvider::rootItem()
{
QMutexLocker locker(&m_mutex);
+ if (m_rootItems.isEmpty())
+ return 0;
return m_rootItems.dequeue();
}
int QHelpContentProvider::nextChildCount() const
{
+ if (m_rootItems.isEmpty())
+ return 0;
return m_rootItems.head()->childCount();
}
@@ -239,8 +247,7 @@ void QHelpContentProvider::run()
QHelpContentItem *item = 0;
m_mutex.lock();
- m_rootItem = new QHelpContentItem(QString(), QString(), 0);
- m_rootItems.enqueue(m_rootItem);
+ QHelpContentItem * const rootItem = new QHelpContentItem(QString(), QString(), 0);
QStringList atts = m_filterAttributes;
const QStringList fileNames = m_helpEngine->orderedFileNameList;
m_mutex.unlock();
@@ -248,9 +255,10 @@ void QHelpContentProvider::run()
foreach (const QString &dbFileName, fileNames) {
m_mutex.lock();
if (m_abort) {
+ delete rootItem;
m_abort = false;
m_mutex.unlock();
- break;
+ return;
}
m_mutex.unlock();
QHelpDBReader reader(dbFileName,
@@ -278,8 +286,8 @@ CHECK_DEPTH:
if (depth == 0) {
m_mutex.lock();
item = new QHelpContentItem(title, link,
- m_helpEngine->fileNameReaderMap.value(dbFileName), m_rootItem);
- m_rootItem->appendChild(item);
+ m_helpEngine->fileNameReaderMap.value(dbFileName), rootItem);
+ rootItem->appendChild(item);
m_mutex.unlock();
stack.push(item);
_depth = 1;
@@ -303,8 +311,10 @@ CHECK_DEPTH:
}
}
m_mutex.lock();
+ m_rootItems.enqueue(rootItem);
m_abort = false;
m_mutex.unlock();
+ emit finishedSuccessFully();
}
@@ -339,9 +349,9 @@ QHelpContentModel::QHelpContentModel(QHelpEnginePrivate *helpEngine)
d->rootItem = 0;
d->qhelpContentProvider = new QHelpContentProvider(helpEngine);
- connect(d->qhelpContentProvider, SIGNAL(finished()),
+ connect(d->qhelpContentProvider, SIGNAL(finishedSuccessFully()),
this, SLOT(insertContents()), Qt::QueuedConnection);
- connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateContents()));
+ connect(helpEngine->q, SIGNAL(readersAboutToBeInvalidated()), this, SLOT(invalidateContents()));
}
/*!
@@ -381,6 +391,9 @@ void QHelpContentModel::createContents(const QString &customFilterName)
void QHelpContentModel::insertContents()
{
+ QHelpContentItem * const newRootItem = d->qhelpContentProvider->rootItem();
+ if (!newRootItem)
+ return;
int count;
if (d->rootItem) {
count = d->rootItem->childCount() - 1;
@@ -392,7 +405,7 @@ void QHelpContentModel::insertContents()
count = d->qhelpContentProvider->nextChildCount() - 1;
beginInsertRows(QModelIndex(), 0, count > 0 ? count : 0);
- d->rootItem = d->qhelpContentProvider->rootItem();
+ d->rootItem = newRootItem;
endInsertRows();
emit contentsCreated();
}
@@ -572,3 +585,5 @@ void QHelpContentWidget::showLink(const QModelIndex &index)
}
QT_END_NAMESPACE
+
+#include "qhelpcontentwidget.moc"
diff --git a/src/assistant/help/qhelpenginecore.cpp b/src/assistant/help/qhelpenginecore.cpp
index 00b55d078..f502e1df4 100644
--- a/src/assistant/help/qhelpenginecore.cpp
+++ b/src/assistant/help/qhelpenginecore.cpp
@@ -71,6 +71,7 @@ QHelpEngineCorePrivate::~QHelpEngineCorePrivate()
void QHelpEngineCorePrivate::clearMaps()
{
+ emit q->readersAboutToBeInvalidated();
QMap<QString, QHelpDBReader*>::iterator it = readerMap.begin();
while (it != readerMap.end()) {
delete it.value();
diff --git a/src/assistant/help/qhelpenginecore.h b/src/assistant/help/qhelpenginecore.h
index bab84ee4e..8579165ff 100644
--- a/src/assistant/help/qhelpenginecore.h
+++ b/src/assistant/help/qhelpenginecore.h
@@ -108,6 +108,7 @@ Q_SIGNALS:
void setupFinished();
void currentFilterChanged(const QString &newFilter);
void warning(const QString &msg);
+ void readersAboutToBeInvalidated();
protected:
QHelpEngineCore(QHelpEngineCorePrivate *helpEngineCorePrivate,
diff --git a/src/assistant/help/qhelpindexwidget.cpp b/src/assistant/help/qhelpindexwidget.cpp
index 7da09de53..33e831017 100644
--- a/src/assistant/help/qhelpindexwidget.cpp
+++ b/src/assistant/help/qhelpindexwidget.cpp
@@ -224,7 +224,7 @@ QHelpIndexModel::QHelpIndexModel(QHelpEnginePrivate *helpEngine)
d = new QHelpIndexModelPrivate(helpEngine);
connect(d->indexProvider, SIGNAL(finished()), this, SLOT(insertIndices()));
- connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateIndex()));
+ connect(helpEngine->q, SIGNAL(readersAboutToBeInvalidated()), this, SLOT(invalidateIndex()));
}
QHelpIndexModel::~QHelpIndexModel()
diff --git a/src/macdeployqt/macdeployqt/main.cpp b/src/macdeployqt/macdeployqt/main.cpp
index 865a22f03..0f38cdfaf 100644
--- a/src/macdeployqt/macdeployqt/main.cpp
+++ b/src/macdeployqt/macdeployqt/main.cpp
@@ -51,6 +51,7 @@ int main(int argc, char **argv)
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
qDebug() << " -qmldir=<path> : Deploy imports used by .qml files in the given path";
qDebug() << " -always-overwrite : Copy files enven if the target file exists";
+ qDebug() << " -codesign=<ident> : Run codesing with the given identity on all executables";
qDebug() << "";
qDebug() << "macdeployqt takes an application bundle as input and makes it";
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
@@ -81,6 +82,8 @@ int main(int argc, char **argv)
extern bool alwaysOwerwriteEnabled;
QStringList additionalExecutables;
QStringList qmlDirs;
+ extern bool runCodesign;
+ extern QString codesignIdentiy;
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
@@ -123,6 +126,15 @@ int main(int argc, char **argv)
} else if (argument == QByteArray("-always-overwrite")) {
LogDebug() << "Argument found:" << argument;
alwaysOwerwriteEnabled = true;
+ } else if (argument.startsWith(QByteArray("-codesign"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf("=");
+ if (index < 0 || index >= argument.size()) {
+ LogError() << "Missing code signing identity";
+ } else {
+ runCodesign = true;
+ codesignIdentiy = argument.mid(index+1);
+ }
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
return 0;
@@ -149,6 +161,9 @@ int main(int argc, char **argv)
if (!qmlDirs.isEmpty())
deployQmlImports(appBundlePath, qmlDirs);
+ if (runCodesign)
+ codesign(codesignIdentiy, appBundlePath);
+
if (dmg) {
LogNormal();
createDiskImage(appBundlePath);
diff --git a/src/macdeployqt/shared/shared.cpp b/src/macdeployqt/shared/shared.cpp
index 314bc2cc5..a8716d753 100644
--- a/src/macdeployqt/shared/shared.cpp
+++ b/src/macdeployqt/shared/shared.cpp
@@ -38,6 +38,7 @@
#include <QDir>
#include <QRegExp>
#include <QSet>
+#include <QStack>
#include <QDirIterator>
#include <QLibraryInfo>
#include <QJsonDocument>
@@ -48,6 +49,8 @@
bool runStripEnabled = true;
bool alwaysOwerwriteEnabled = false;
+bool runCodesign = false;
+QString codesignIdentiy;
int logLevel = 1;
using std::cout;
@@ -70,7 +73,8 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info)
debug << "Install name" << info.installName << "\n";
debug << "Deployed install name" << info.deployedInstallName << "\n";
debug << "Source file Path" << info.sourceFilePath << "\n";
- debug << "Destination Directory (relative to bundle)" << info.destinationDirectory << "\n";
+ debug << "Framework Destination Directory (relative to bundle)" << info.frameworkDestinationDirectory << "\n";
+ debug << "Binary Destination Directory (relative to bundle)" << info.binaryDestinationDirectory << "\n";
return debug;
}
@@ -123,6 +127,38 @@ bool copyFilePrintStatus(const QString &from, const QString &to)
}
}
+bool linkFilePrintStatus(const QString &file, const QString &link)
+{
+ if (QFile(link).exists()) {
+ if (QFile(link).symLinkTarget().isEmpty())
+ LogError() << link << "exists but it's a file.";
+ else
+ LogNormal() << "Symlink exists, skipping:" << link;
+ return false;
+ } else if (QFile::link(file, link)) {
+ LogNormal() << " symlink" << link;
+ LogNormal() << " points to" << file;
+ return true;
+ } else {
+ LogError() << "failed to symlink" << link;
+ LogError() << " to" << file;
+ return false;
+ }
+}
+
+void patch_debugInInfoPlist(const QString &infoPlistPath)
+{
+ // Older versions of qmake may have the "_debug" binary as
+ // the value for CFBundleExecutable. Remove it.
+ QFile infoPlist(infoPlistPath);
+ infoPlist.open(QIODevice::ReadOnly);
+ QByteArray contents = infoPlist.readAll();
+ infoPlist.close();
+ infoPlist.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ contents.replace("_debug", ""); // surely there are no legit uses of "_debug" in an Info.plist
+ infoPlist.write(contents);
+}
+
FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs)
{
FrameworkInfo info;
@@ -182,19 +218,22 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs)
// remove ".framework"
name = currentPart;
name.chop(QString(".framework").length());
+ info.isDylib = false;
info.frameworkName = currentPart;
state = Version;
++part;
continue;
} if (state == DylibName) {
name = currentPart.split(" (compatibility").at(0);
+ info.isDylib = true;
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.frameworkDestinationDirectory = bundleFrameworkDirectory + "/";
+ info.binaryDestinationDirectory = info.frameworkDestinationDirectory;
info.binaryDirectory = info.frameworkDirectory;
info.binaryPath = info.frameworkPath;
state = End;
@@ -209,7 +248,8 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs)
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;
+ info.frameworkDestinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName;
+ info.binaryDestinationDirectory = info.frameworkDestinationDirectory + "/" + info.binaryDirectory;
state = End;
} else if (state == End) {
break;
@@ -244,6 +284,22 @@ QStringList findAppLibraries(const QString &appBundlePath)
return result;
}
+QStringList findAppBundleFiles(const QString &appBundlePath)
+{
+ QStringList result;
+
+ QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*"),
+ QDir::Files, QDirIterator::Subdirectories);
+
+ while (iter.hasNext()) {
+ iter.next();
+ if (iter.fileInfo().isSymLink())
+ continue;
+ result << iter.fileInfo().filePath();
+ }
+
+ return result;
+}
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs)
{
@@ -257,7 +313,7 @@ QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebu
}
}
return libraries;
-}
+};
QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs)
{
@@ -295,6 +351,35 @@ QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, bool useD
return result;
}
+QStringList getBinaryDependencies(const QString executablePath, const QString &path)
+{
+ QStringList binaries;
+
+ 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
+
+ // return bundle-local dependencies. (those starting with @executable_path)
+ foreach (const QString &line, outputLines) {
+ QString trimmedLine = line.mid(0, line.indexOf("(")).trimmed(); // remove "(compatibility version ...)" and whitespace
+ if (trimmedLine.startsWith("@executable_path/")) {
+ QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
+ if (binary != path)
+ binaries.append(binary);
+ }
+ }
+
+ return binaries;
+}
+
// copies everything _inside_ sourcePath to destinationPath
void recursiveCopy(const QString &sourcePath, const QString &destinationPath)
{
@@ -324,12 +409,37 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QString &sourceP
QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
foreach (QString file, files) {
const QString fileSourcePath = sourcePath + QLatin1Char('/') + file;
- const QString fileDestinationPath = destinationPath + QLatin1Char('/') + file;
if (file.endsWith("_debug.dylib")) {
continue; // Skip debug versions
} else if (file.endsWith(QStringLiteral(".dylib"))) {
+ // App store code signing rules forbids code binaries in Contents/Resources/,
+ // which poses a problem for deploying mixed .qml/.dylib Qt Quick imports.
+ // Solve this by placing the dylibs in Contents/PlugIns/quick, and then
+ // creting a symlink to there from the Qt Quick import in Contents/Resources/.
+ //
+ // Example:
+ // MyApp.app/Contents/Resources/qml/QtQuick/Controls/libqtquickcontrolsplugin.dylib ->
+ // ../../../../PlugIns/quick/libqtquickcontrolsplugin.dylib
+ //
+
+ // The .dylib destination path:
+ QString fileDestinationDir = appBundlePath + QStringLiteral("/Contents/PlugIns/quick/");
+ QDir().mkpath(fileDestinationDir);
+ QString fileDestinationPath = fileDestinationDir + file;
+
+ // The .dylib symlink destination path:
+ QString linkDestinationPath = destinationPath + QLatin1Char('/') + file;
+
+ // The (relative) link; with a correct number of "../"'s.
+ QString linkPath = QStringLiteral("PlugIns/quick/") + file;
+ int cdupCount = linkDestinationPath.count(QStringLiteral("/"));
+ for (int i = 0; i < cdupCount - 2; ++i)
+ linkPath.prepend("../");
+
if (copyFilePrintStatus(fileSourcePath, fileDestinationPath)) {
+ linkFilePrintStatus(linkPath, linkDestinationPath);
+
runStrip(fileDestinationPath);
bool useDebugLibs = false;
bool useLoaderPath = false;
@@ -337,6 +447,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QString &sourceP
deployQtFrameworks(frameworks, appBundlePath, QStringList(fileDestinationPath), useDebugLibs, useLoaderPath);
}
} else {
+ QString fileDestinationPath = destinationPath + QLatin1Char('/') + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
}
}
@@ -347,56 +458,87 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QString &sourceP
}
}
-
-QString copyFramework(const FrameworkInfo &framework, const QString path)
+QString copyDylib(const FrameworkInfo &framework, const QString path)
{
- QString from = framework.sourceFilePath;
-
- if (!QFile::exists(from)) {
- LogError() << "no file at" << from;
+ if (!QFile::exists(framework.sourceFilePath)) {
+ LogError() << "no file at" << framework.sourceFilePath;
return QString();
}
- QFileInfo fromDirInfo(framework.frameworkPath + QLatin1Char('/')
- + framework.binaryDirectory);
- bool fromDirIsSymLink = fromDirInfo.isSymLink();
- QString unresolvedToDir = path + QLatin1Char('/') + framework.destinationDirectory;
- QString resolvedToDir;
- QString relativeLinkTarget; // will contain the link from Current to e.g. 4 in the Versions directory
- if (fromDirIsSymLink) {
- // handle the case where framework is referenced with Versions/Current
- // which is a symbolic link, so copy to target and recreate as symbolic link
- relativeLinkTarget = QDir(fromDirInfo.canonicalPath())
- .relativeFilePath(QFileInfo(fromDirInfo.symLinkTarget()).canonicalFilePath());
- resolvedToDir = QFileInfo(unresolvedToDir).path() + QLatin1Char('/') + relativeLinkTarget;
- } else {
- resolvedToDir = unresolvedToDir;
+ // Construct destination paths. The full path typically looks like
+ // MyApp.app/Contents/Frameworks/libfoo.dylib
+ QString dylibDestinationDirectory = path + QLatin1Char('/') + framework.frameworkDestinationDirectory;
+ QString dylibDestinationBinaryPath = dylibDestinationDirectory + QLatin1Char('/') + framework.binaryName;
+
+ // Create destination directory
+ if (!QDir().mkpath(dylibDestinationDirectory)) {
+ LogError() << "could not create destination directory" << dylibDestinationDirectory;
+ return QString();
}
- QString to = resolvedToDir + "/" + framework.binaryName;
+ // Retrun if the dylib has aleardy been deployed
+ if (QFileInfo(dylibDestinationBinaryPath).exists() && !alwaysOwerwriteEnabled)
+ return dylibDestinationBinaryPath;
+
+ // Copy dylib binary
+ copyFilePrintStatus(framework.sourceFilePath, dylibDestinationBinaryPath);
+ return dylibDestinationBinaryPath;
+}
- // create the (non-symlink) dir
- QDir dir;
- if (!dir.mkpath(resolvedToDir)) {
- LogError() << "could not create destination directory" << to;
+QString copyFramework(const FrameworkInfo &framework, const QString path)
+{
+ if (!QFile::exists(framework.sourceFilePath)) {
+ LogError() << "no file at" << framework.sourceFilePath;
return QString();
}
- if (!QFile::exists(to) || alwaysOwerwriteEnabled) { // copy the binary and resources if that wasn't done before
- copyFilePrintStatus(from, to);
+ // Construct destination paths. The full path typically looks like
+ // MyApp.app/Contents/Frameworks/Foo.framework/Versions/5/QtFoo
+ QString frameworkDestinationDirectory = path + QLatin1Char('/') + framework.frameworkDestinationDirectory;
+ QString frameworkBinaryDestinationDirectory = frameworkDestinationDirectory + QLatin1Char('/') + framework.binaryDirectory;
+ QString frameworkDestinationBinaryPath = frameworkBinaryDestinationDirectory + QLatin1Char('/') + framework.binaryName;
- const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
- const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources";
- recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
+ // Return if the framework has aleardy been deployed
+ if (QDir(frameworkDestinationDirectory).exists() && !alwaysOwerwriteEnabled)
+ return QString();
+
+ // Create destination directory
+ if (!QDir().mkpath(frameworkBinaryDestinationDirectory)) {
+ LogError() << "could not create destination directory" << frameworkBinaryDestinationDirectory;
+ return QString();
}
- // create the Versions/Current symlink dir if necessary
- if (fromDirIsSymLink) {
- QFile::link(relativeLinkTarget, unresolvedToDir);
- LogNormal() << " linked:" << unresolvedToDir;
- LogNormal() << " to" << resolvedToDir << "(" << relativeLinkTarget << ")";
+ // Now copy the framework. Some parts should be left out (headers/, .prl files).
+ // Some parts should be included (Resources/, symlink structure). We want this
+ // function to make as few assumtions about the framework as possible while at
+ // the same time producing a codesign-compatible framework.
+
+ // Copy framework binary
+ copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath);
+
+ // Copy Resouces/
+ const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
+ const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
+ recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
+
+ // Create symlink structure. Links at the framework root point to Versions/Current/
+ // which again points to the actual version:
+ // QtFoo.framework/QtFoo -> Versions/Current/QtFoo
+ // QtFoo.framework/Resources -> Versions/Current/Resources
+ // QtFoo.framework/Versions/Current -> 5
+ linkFilePrintStatus("Versions/Current/" + framework.binaryName, frameworkDestinationDirectory + "/" + framework.binaryName);
+ linkFilePrintStatus("Versions/Current/Resources", frameworkDestinationDirectory + "/Resources");
+ linkFilePrintStatus(framework.version, frameworkDestinationDirectory + "/Versions/Current");
+
+ // Correct Info.plist location for frameworks produced by older versions of qmake
+ // Contents/Info.plist should be Versions/5/Resources/Info.plist
+ const QString legacyInfoPlistPath = framework.frameworkPath + "/Contents/Info.plist";
+ const QString correctInfoPlistPath = frameworkDestinationDirectory + "/Resources/Info.plist";
+ if (QFile(legacyInfoPlistPath).exists()) {
+ copyFilePrintStatus(legacyInfoPlistPath, correctInfoPlistPath);
+ patch_debugInInfoPlist(correctInfoPlistPath);
}
- return to;
+ return frameworkDestinationBinaryPath;
}
void runInstallNameTool(QStringList options)
@@ -425,7 +567,7 @@ void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework
QString deployedInstallName;
if (useLoaderPath) {
deployedInstallName = QLatin1String("@loader_path/")
- + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + QLatin1Char('/') + framework.destinationDirectory + QLatin1Char('/') + framework.binaryName);
+ + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + QLatin1Char('/') + framework.binaryDestinationDirectory + QLatin1Char('/') + framework.binaryName);
} else {
deployedInstallName = framework.deployedInstallName;
}
@@ -495,8 +637,9 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
// Install_name_tool the new id into the binaries
changeInstallName(bundlePath, framework, binaryPaths, useLoaderPath);
- // Copy farmework to app bundle.
- const QString deployedBinaryPath = copyFramework(framework, bundlePath);
+ // Copy the framework/dylib to the app bundle.
+ const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath)
+ : copyFramework(framework, bundlePath);
// Skip the rest if already was deployed.
if (deployedBinaryPath.isNull())
continue;
@@ -505,6 +648,8 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
// Install_name_tool it a new id.
changeIdentification(framework.deployedInstallName, deployedBinaryPath);
+
+
// Check for framework dependencies
QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs);
@@ -617,8 +762,10 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
if (copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
+
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs);
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
+
}
}
}
@@ -667,6 +814,12 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
void deployQmlImport(const QString &appBundlePath, const QString &importSourcePath, const QString &importName)
{
QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
+
+ // Skip already deployed imports. This can happen in cases like "QtQuick.Controls.Styles",
+ // where deploying QtQuick.Controls will also deploy the "Styles" sub-import.
+ if (QDir().exists(importDestinationPath))
+ return;
+
recursiveCopyAndDeploy(appBundlePath, importSourcePath, importDestinationPath);
}
@@ -796,6 +949,85 @@ void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDe
}
}
+void codesignFile(const QString &identity, const QString &filePath)
+{
+ if (!runCodesign)
+ return;
+
+ LogNormal() << "codesign" << filePath;
+
+ QProcess codesign;
+ codesign.start("codesign", QStringList() << "--preserve-metadata=identifier,entitlements,resource-rules"
+ << "--force" << "-s" << identity << filePath);
+ codesign.waitForFinished(-1);
+
+ QByteArray err = codesign.readAllStandardError();
+ if (codesign.exitCode() > 0) {
+ LogError() << "Codesign signing error:";
+ LogError() << err;
+ } else if (!err.isEmpty()) {
+ LogDebug() << err;
+ }
+}
+
+void codesign(const QString &identity, const QString &appBundlePath)
+{
+ // Code sign all binaries in the app bundle. This needs to
+ // be done inside-out, e.g sign framework dependencies
+ // before the main app binary. The codesign tool itself has
+ // a "--deep" option to do this, but usage when signing is
+ // not recommended: "Signing with --deep is for emergency
+ // repairs and temporary adjustments only."
+
+ LogNormal() << "";
+ LogNormal() << "Signing" << appBundlePath << "with identity" << identity;
+
+ QStack<QString> pendingBinaries;
+ QSet<QString> signedBinaries;
+
+ // Create the root code-binary set. This set consists of the application
+ // executable(s) and the plugins.
+ QString rootBinariesPath = appBundlePath + "/Contents/MacOS/";
+ QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files);
+ foreach (const QString &binary, foundRootBinaries)
+ pendingBinaries.push(rootBinariesPath + binary);
+
+ QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/");
+ foreach (const QString &binary, foundPluginBinaries)
+ pendingBinaries.push(binary);
+
+
+ // Sign all binares; use otool to find and sign dependencies first.
+ while (!pendingBinaries.isEmpty()) {
+ QString binary = pendingBinaries.pop();
+ if (signedBinaries.contains(binary))
+ continue;
+
+ // Check if there are unsigned dependencies, sign these first
+ QStringList dependencies = getBinaryDependencies(rootBinariesPath, binary).toSet().subtract(signedBinaries).toList();
+ if (!dependencies.isEmpty()) {
+ pendingBinaries.push(binary);
+ foreach (const QString &dependency, dependencies)
+ pendingBinaries.push(dependency);
+ continue;
+ }
+ // All dependencies are signed, now sign this binary
+ codesignFile(identity, binary);
+ signedBinaries.insert(binary);
+ }
+
+ // Verify code signature
+ QProcess codesign;
+ codesign.start("codesign", QStringList() << "--deep" << "-v" << appBundlePath);
+ codesign.waitForFinished(-1);
+ QByteArray err = codesign.readAllStandardError();
+ if (codesign.exitCode() > 0) {
+ LogError() << "codesign verification error:";
+ LogError() << err;
+ } else if (!err.isEmpty()) {
+ LogDebug() << err;
+ }
+}
void createDiskImage(const QString &appBundlePath)
{
diff --git a/src/macdeployqt/shared/shared.h b/src/macdeployqt/shared/shared.h
index 9a8ff1786..07e4914c5 100644
--- a/src/macdeployqt/shared/shared.h
+++ b/src/macdeployqt/shared/shared.h
@@ -48,6 +48,7 @@ extern bool runStripEnabled;
class FrameworkInfo
{
public:
+ bool isDylib;
QString frameworkDirectory;
QString frameworkName;
QString frameworkPath;
@@ -58,7 +59,8 @@ public:
QString installName;
QString deployedInstallName;
QString sourceFilePath;
- QString destinationDirectory;
+ QString frameworkDestinationDirectory;
+ QString binaryDestinationDirectory;
};
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
@@ -101,6 +103,8 @@ void changeIdentification(const QString &id, const QString &binaryPath);
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
void runStrip(const QString &binaryPath);
QString findAppBinary(const QString &appBundlePath);
+void codesignFile(const QString &identity, const QString &filePath);
+void codesign(const QString &identity, const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath);
diff --git a/src/qconfig/main.cpp b/src/qconfig/main.cpp
index 7d0d8e8f6..db6b0a673 100644
--- a/src/qconfig/main.cpp
+++ b/src/qconfig/main.cpp
@@ -511,7 +511,7 @@ void Main::about()
"<p><b><font size=\"+2\">Qtopia Core build configuration</font></b></p>"
"<p></p>"
"<p>Version 2.0</p>"
- "<p>Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).</p>"
+ "<p>Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).</p>"
"<p></p>"
);
}