summaryrefslogtreecommitdiffstats
path: root/src/tools/macdeployqt/shared/shared.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/macdeployqt/shared/shared.cpp')
-rw-r--r--src/tools/macdeployqt/shared/shared.cpp154
1 files changed, 78 insertions, 76 deletions
diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp
index 584b541c9a..6ff269b36d 100644
--- a/src/tools/macdeployqt/shared/shared.cpp
+++ b/src/tools/macdeployqt/shared/shared.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QString>
#include <QStringList>
@@ -34,6 +9,8 @@
#include <QProcess>
#include <QDir>
#include <QSet>
+#include <QVariant>
+#include <QVariantMap>
#include <QStack>
#include <QDirIterator>
#include <QLibraryInfo>
@@ -62,6 +39,7 @@ bool deployFramework = false;
using std::cout;
using std::endl;
+using namespace Qt::StringLiterals;
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b)
{
@@ -174,7 +152,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
LogDebug() << " inspecting" << binaryPath;
QProcess otool;
otool.start("otool", QStringList() << "-L" << binaryPath);
- otool.waitForFinished();
+ otool.waitForFinished(-1);
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
LogError() << otool.readAllStandardError();
@@ -183,7 +161,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
static const QRegularExpression regexp(QStringLiteral(
"^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), "
- "current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$"));
+ "current version (\\d+\\.\\d+\\.\\d+)(, weak|, reexport)?\\)$"));
QString output = otool.readAllStandardOutput();
QStringList outputLines = output.split("\n", Qt::SkipEmptyParts);
@@ -194,7 +172,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
outputLines.removeFirst(); // remove line containing the binary path
if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) {
- const auto match = regexp.match(outputLines.first());
+ const auto match = regexp.match(outputLines.constFirst());
if (match.hasMatch()) {
QString installname = match.captured(1);
if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) {
@@ -206,7 +184,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
info.installName = binaryPath;
}
} else {
- LogDebug() << "Could not parse otool output line:" << outputLines.first();
+ LogDebug() << "Could not parse otool output line:" << outputLines.constFirst();
outputLines.removeFirst();
}
}
@@ -214,6 +192,8 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
for (const QString &outputLine : outputLines) {
const auto match = regexp.match(outputLine);
if (match.hasMatch()) {
+ if (match.captured(1) == info.installName)
+ continue; // Another arch reference to the same binary
DylibInfo dylib;
dylib.binaryPath = match.captured(1);
dylib.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
@@ -307,15 +287,15 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
for (QString &path : librarySearchPath) {
if (!path.endsWith("/"))
path += '/';
- QString nameInPath = path + parts.join(QLatin1Char('/'));
+ QString nameInPath = path + parts.join(u'/');
if (QFile::exists(nameInPath)) {
- info.frameworkDirectory = path + partsCopy.join(QLatin1Char('/'));
+ info.frameworkDirectory = path + partsCopy.join(u'/');
break;
}
}
if (currentPart.contains(".framework")) {
if (info.frameworkDirectory.isEmpty())
- info.frameworkDirectory = "/Library/Frameworks/" + partsCopy.join(QLatin1Char('/'));
+ info.frameworkDirectory = "/Library/Frameworks/" + partsCopy.join(u'/');
if (!info.frameworkDirectory.endsWith("/"))
info.frameworkDirectory += "/";
state = FrameworkName;
@@ -323,7 +303,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
continue;
} else if (currentPart.contains(".dylib")) {
if (info.frameworkDirectory.isEmpty())
- info.frameworkDirectory = "/usr/lib/" + partsCopy.join(QLatin1Char('/'));
+ info.frameworkDirectory = "/usr/lib/" + partsCopy.join(u'/');
if (!info.frameworkDirectory.endsWith("/"))
info.frameworkDirectory += "/";
state = DylibName;
@@ -455,7 +435,7 @@ QStringList findAppLibraries(const QString &appBundlePath)
{
QStringList result;
// dylibs
- QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*.dylib"),
+ QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*.dylib") << QString::fromLatin1("*.so"),
QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
while (iter.hasNext()) {
iter.next();
@@ -618,10 +598,14 @@ QStringList getBinaryDependencies(const QString executablePath,
QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
if (binary != path)
binaries.append(binary);
+ } else if (trimmedLine.startsWith("@loader_path/")) {
+ QString binary = QDir::cleanPath(QFileInfo(path).path() + "/" + trimmedLine.mid(QStringLiteral("@loader_path/").length()));
+ if (binary != path)
+ binaries.append(binary);
} else if (trimmedLine.startsWith("@rpath/")) {
if (!rpathsLoaded) {
rpaths = getBinaryRPaths(path, true, executablePath);
- foreach (const QString &binaryPath, additionalBinariesContainingRpaths)
+ for (const QString &binaryPath : additionalBinariesContainingRpaths)
rpaths += getBinaryRPaths(binaryPath, true);
rpaths.removeDuplicates();
rpathsLoaded = true;
@@ -647,22 +631,27 @@ QStringList getBinaryDependencies(const QString executablePath,
}
// copies everything _inside_ sourcePath to destinationPath
-bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
+bool recursiveCopy(const QString &sourcePath, const QString &destinationPath,
+ const QRegularExpression &ignoreRegExp = QRegularExpression())
{
- if (!QDir(sourcePath).exists())
+ const QDir sourceDir(sourcePath);
+ if (!sourceDir.exists())
return false;
QDir().mkpath(destinationPath);
LogNormal() << "copy:" << sourcePath << destinationPath;
- QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot);
+ const QStringList files = sourceDir.entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot);
+ const bool hasValidRegExp = ignoreRegExp.isValid() && ignoreRegExp.pattern().length() > 0;
for (const QString &file : files) {
+ if (hasValidRegExp && ignoreRegExp.match(file).hasMatch())
+ continue;
const QString fileSourcePath = sourcePath + "/" + file;
const QString fileDestinationPath = destinationPath + "/" + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
}
- QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
+ const QStringList subdirs = sourceDir.entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &dir : subdirs) {
recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir);
}
@@ -676,9 +665,11 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &
LogNormal() << "copy:" << sourcePath << destinationPath;
const bool isDwarfPath = sourcePath.endsWith("DWARF");
- QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
+ const QDir sourceDir(sourcePath);
+
+ const QStringList files = sourceDir.entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
for (const QString &file : files) {
- const QString fileSourcePath = sourcePath + QLatin1Char('/') + file;
+ const QString fileSourcePath = sourcePath + u'/' + file;
if (file.endsWith("_debug.dylib")) {
continue; // Skip debug versions
@@ -699,7 +690,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &
QString fileDestinationPath = fileDestinationDir + file;
// The .dylib symlink destination path:
- QString linkDestinationPath = destinationPath + QLatin1Char('/') + file;
+ QString linkDestinationPath = destinationPath + u'/' + file;
// The (relative) link; with a correct number of "../"'s.
QString linkPath = QStringLiteral("PlugIns/quick/") + file;
@@ -717,14 +708,14 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &
deployQtFrameworks(frameworks, appBundlePath, QStringList(fileDestinationPath), useDebugLibs, useLoaderPath);
}
} else {
- QString fileDestinationPath = destinationPath + QLatin1Char('/') + file;
+ QString fileDestinationPath = destinationPath + u'/' + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
}
}
- QStringList subdirs = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot);
+ const QStringList subdirs = sourceDir.entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &dir : subdirs) {
- recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + QLatin1Char('/') + dir, destinationPath + QLatin1Char('/') + dir);
+ recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + u'/' + dir, destinationPath + u'/' + dir);
}
}
@@ -737,8 +728,8 @@ QString copyDylib(const FrameworkInfo &framework, const QString path)
// 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;
+ QString dylibDestinationDirectory = path + u'/' + framework.frameworkDestinationDirectory;
+ QString dylibDestinationBinaryPath = dylibDestinationDirectory + u'/' + framework.binaryName;
// Create destination directory
if (!QDir().mkpath(dylibDestinationDirectory)) {
@@ -764,9 +755,9 @@ QString copyFramework(const FrameworkInfo &framework, const QString path)
// 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;
+ QString frameworkDestinationDirectory = path + u'/' + framework.frameworkDestinationDirectory;
+ QString frameworkBinaryDestinationDirectory = frameworkDestinationDirectory + u'/' + framework.binaryDirectory;
+ QString frameworkDestinationBinaryPath = frameworkBinaryDestinationDirectory + u'/' + framework.binaryName;
// Return if the framework has aleardy been deployed
if (QDir(frameworkDestinationDirectory).exists() && !alwaysOwerwriteEnabled)
@@ -788,14 +779,16 @@ QString copyFramework(const FrameworkInfo &framework, const QString path)
// Copy Resources/, Libraries/ and Helpers/
const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
- const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
- recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
+ const QString resourcesDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
+ // Ignore *.prl files that are in the Resources directory
+ recursiveCopy(resourcesSourcePath, resourcesDestinationPath,
+ QRegularExpression("\\A(?:[^/]*\\.prl)\\z"));
const QString librariesSourcePath = framework.frameworkPath + "/Libraries";
- const QString librariesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Libraries";
- bool createdLibraries = recursiveCopy(librariesSourcePath, librariesDestianationPath);
+ const QString librariesDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Libraries";
+ bool createdLibraries = recursiveCopy(librariesSourcePath, librariesDestinationPath);
const QString helpersSourcePath = framework.frameworkPath + "/Helpers";
- const QString helpersDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Helpers";
- bool createdHelpers = recursiveCopy(helpersSourcePath, helpersDestianationPath);
+ const QString helpersDestinationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Helpers";
+ bool createdHelpers = recursiveCopy(helpersSourcePath, helpersDestinationPath);
// Create symlink structure. Links at the framework root point to Versions/Current/
// which again points to the actual version:
@@ -846,17 +839,23 @@ void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework
for (const QString &binary : binaryPaths) {
QString deployedInstallName;
if (useLoaderPath) {
- deployedInstallName = QLatin1String("@loader_path/")
- + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + QLatin1Char('/') + framework.binaryDestinationDirectory + QLatin1Char('/') + framework.binaryName);
+ deployedInstallName = "@loader_path/"_L1
+ + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + u'/' + framework.binaryDestinationDirectory + u'/' + framework.binaryName);
} else {
deployedInstallName = framework.deployedInstallName;
}
changeInstallName(framework.installName, deployedInstallName, binary);
// Workaround for the case when the library ID name is a symlink, while the dependencies
// specified using the canonical path to the library (QTBUG-56814)
- QString canonicalInstallName = QFileInfo(framework.installName).canonicalFilePath();
+ QFileInfo fileInfo= QFileInfo(framework.installName);
+ QString canonicalInstallName = fileInfo.canonicalFilePath();
if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) {
changeInstallName(canonicalInstallName, deployedInstallName, binary);
+ // some libraries' inner dependencies (such as ffmpeg, nettle) use symbol link (QTBUG-100093)
+ QString innerDependency = fileInfo.canonicalPath() + "/" + fileInfo.fileName();
+ if (innerDependency != canonicalInstallName && innerDependency != framework.installName) {
+ changeInstallName(innerDependency, deployedInstallName, binary);
+ }
}
}
}
@@ -869,9 +868,9 @@ void addRPath(const QString &rpath, const QString &binaryPath)
void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
{
const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath()
- + QLatin1String("/Contents/Frameworks");
+ + "/Contents/Frameworks"_L1;
const QString relativeFrameworkPath = QFileInfo(binaryPath).absoluteDir().relativeFilePath(absFrameworksPath);
- const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath;
+ const QString loaderPathToFrameworks = "@loader_path/"_L1 + relativeFrameworkPath;
bool rpathToFrameworksFound = false;
QStringList args;
QList<QString> binaryRPaths = getBinaryRPaths(binaryPath, false);
@@ -882,7 +881,8 @@ void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const
continue;
}
if (rpaths.contains(resolveDyldPrefix(rpath, binaryPath, binaryPath))) {
- args << "-delete_rpath" << rpath;
+ if (!args.contains(rpath))
+ args << "-delete_rpath" << rpath;
}
}
if (!args.length()) {
@@ -941,18 +941,18 @@ void stripAppBinary(const QString &bundlePath)
bool DeploymentInfo::containsModule(const QString &module, const QString &libInFix) const
{
// Check for framework first
- if (deployedFrameworks.contains(QLatin1String("Qt") + module + libInFix +
- QLatin1String(".framework"))) {
+ if (deployedFrameworks.contains("Qt"_L1 + module + libInFix + ".framework"_L1))
return true;
- }
// Check for dylib
- const QRegularExpression dylibRegExp(QLatin1String("libQt[0-9]+") + module +
- libInFix + QLatin1String(".[0-9]+.dylib"));
+ const QRegularExpression dylibRegExp("libQt[0-9]+"_L1
+ + module + libInFix
+ + (isDebug ? "_debug" : "")
+ + ".[0-9]+.dylib"_L1);
return deployedFrameworks.filter(dylibRegExp).size() > 0;
}
/*
- Deploys the the listed frameworks listed into an app bundle.
+ Deploys the listed frameworks 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
@@ -1089,13 +1089,13 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
const auto addPlugins = [&pluginSourcePath,&pluginList,useDebugLibs](const QString &subDirectory,
const std::function<bool(QString)> &predicate = std::function<bool(QString)>()) {
- const QStringList libs = QDir(pluginSourcePath + QLatin1Char('/') + subDirectory)
+ const QStringList libs = QDir(pluginSourcePath + u'/' + subDirectory)
.entryList({QStringLiteral("*.dylib")});
for (const QString &lib : libs) {
if (lib.endsWith(QStringLiteral("_debug.dylib")) != useDebugLibs)
continue;
if (!predicate || predicate(lib))
- pluginList.append(subDirectory + QLatin1Char('/') + lib);
+ pluginList.append(subDirectory + u'/' + lib);
}
};
@@ -1117,8 +1117,10 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
const QString libInfix = getLibInfix(deploymentInfo.deployedFrameworks);
// Network
- if (deploymentInfo.containsModule("Network", libInfix))
+ if (deploymentInfo.containsModule("Network", libInfix)) {
addPlugins(QStringLiteral("tls"));
+ addPlugins(QStringLiteral("networkinformation"));
+ }
// All image formats (svg if QtSvg is used)
const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
@@ -1169,7 +1171,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
}
static const std::map<QString, std::vector<QString>> map {
- {QStringLiteral("Multimedia"), {QStringLiteral("mediaservice"), QStringLiteral("audio")}},
+ {QStringLiteral("Multimedia"), {QStringLiteral("multimedia")}},
{QStringLiteral("3DRender"), {QStringLiteral("sceneparsers"), QStringLiteral("geometryloaders"), QStringLiteral("renderers")}},
{QStringLiteral("3DQuickRender"), {QStringLiteral("renderplugins")}},
{QStringLiteral("Positioning"), {QStringLiteral("position")}},
@@ -1358,10 +1360,10 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
// Create the destination path from the name
// and version (grabbed from the source path)
// ### let qmlimportscanner provide this.
- name.replace(QLatin1Char('.'), QLatin1Char('/'));
+ name.replace(u'.', u'/');
int secondTolast = path.length() - 2;
QString version = path.mid(secondTolast);
- if (version.startsWith(QLatin1Char('.')))
+ if (version.startsWith(u'.'))
name.append(version);
deployQmlImport(appBundlePath, deploymentInfo.rpathsUsed, path, name);