diff options
30 files changed, 1 insertions, 6799 deletions
diff --git a/conanfile.py b/conanfile.py index a244b2507..26be27021 100644 --- a/conanfile.py +++ b/conanfile.py @@ -37,14 +37,12 @@ _qttools_features = [ "distancefieldgenerator", "kmap2qmap", "linguist", - "macdeployqt", "pixeltool", "qdbus", "qev", "qtattributionsscanner", "qtdiag", "qtplugininfo", - "windeployqt", ] @@ -67,7 +65,7 @@ class QtTools(ConanFile): "Qt Tools run on all the supported development platforms and facilitate " "the development and design of applications." ) - topics = "qt", "qt6", "qttools", "assistant", "designer", "qdoc", "macdeployqt", "windeployqt" + topics = "qt", "qt6", "qttools", "assistant", "designer", "qdoc", settings = "os", "compiler", "arch", "build_type" # for referencing the version number and prerelease tag and dependencies info exports = ".cmake.conf", "dependencies.yaml" diff --git a/configure.cmake b/configure.cmake index 67dfa0193..04e717f44 100644 --- a/configure.cmake +++ b/configure.cmake @@ -1,5 +1,3 @@ - - #### Inputs @@ -69,11 +67,6 @@ qt_feature("linguist" PRIVATE LABEL "Qt Linguist" PURPOSE "Qt Linguist can be used by translator to translate text in Qt applications." ) -qt_feature("macdeployqt" PRIVATE - LABEL "Mac Deployment Tool" - PURPOSE "The Mac deployment tool automates the process of creating a deployable application bundle that contains the Qt libraries as private frameworks." - CONDITION MACOS -) qt_feature("pixeltool" PRIVATE LABEL "pixeltool" PURPOSE "The Qt Pixel Zooming Tool is a graphical application that magnifies the screen around the mouse pointer so you can look more closely at individual pixels." @@ -103,11 +96,6 @@ qt_feature("qtplugininfo" PRIVATE PURPOSE "qtplugininfo dumps metadata about Qt plugins in JSON format." CONDITION QT_FEATURE_commandlineparser AND QT_FEATURE_library AND (android_app OR NOT ANDROID) ) -qt_feature("windeployqt" PRIVATE - LABEL "Windows deployment tool" - PURPOSE "The Windows deployment tool is designed to automate the process of creating a deployable folder containing the Qt-related dependencies (libraries, QML imports, plugins, and translations) required to run the application from that folder. It creates a sandbox for Universal Windows Platform (UWP) or an installation tree for Windows desktop applications, which can be easily bundled into an installation package." - CONDITION WIN32 -) qt_configure_add_summary_section(NAME "Qt Tools") qt_configure_add_summary_entry(ARGS "assistant") qt_configure_add_summary_entry(ARGS "clang") @@ -116,14 +104,12 @@ qt_configure_add_summary_entry(ARGS "designer") qt_configure_add_summary_entry(ARGS "distancefieldgenerator") #qt_configure_add_summary_entry(ARGS "kmap2qmap") qt_configure_add_summary_entry(ARGS "linguist") -qt_configure_add_summary_entry(ARGS "macdeployqt") qt_configure_add_summary_entry(ARGS "pixeltool") qt_configure_add_summary_entry(ARGS "qdbus") #qt_configure_add_summary_entry(ARGS "qev") qt_configure_add_summary_entry(ARGS "qtattributionsscanner") qt_configure_add_summary_entry(ARGS "qtdiag") qt_configure_add_summary_entry(ARGS "qtplugininfo") -qt_configure_add_summary_entry(ARGS "windeployqt") qt_configure_end_summary_section() # end of "Qt Tools" section qt_configure_add_report_entry( TYPE WARNING diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef50f7905..cb0c21a70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,15 +47,9 @@ endif() if(QT_FEATURE_clang AND QT_FEATURE_commandlineparser AND QT_FEATURE_thread) add_subdirectory(qdoc) endif() -if(QT_FEATURE_macdeployqt) - add_subdirectory(macdeployqt) -endif() if(QT_FEATURE_qdbus) add_subdirectory(qdbus) endif() -if(QT_FEATURE_windeployqt) # special case - add_subdirectory(windeployqt) -endif() if(QT_FEATURE_qtdiag) add_subdirectory(qtdiag) endif() diff --git a/src/macdeployqt/CMakeLists.txt b/src/macdeployqt/CMakeLists.txt deleted file mode 100644 index cbce4e4ca..000000000 --- a/src/macdeployqt/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Generated from macdeployqt.pro. - -if(NOT QT_FEATURE_macdeployqt) - return() -endif() -add_subdirectory(macdeployqt) diff --git a/src/macdeployqt/macdeployqt/CMakeLists.txt b/src/macdeployqt/macdeployqt/CMakeLists.txt deleted file mode 100644 index 0c63a877f..000000000 --- a/src/macdeployqt/macdeployqt/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Generated from macdeployqt.pro. - -##################################################################### -## macdeployqt App: -##################################################################### - -qt_internal_add_app(macdeployqt - SOURCES - ../shared/shared.cpp - main.cpp - PUBLIC_LIBRARIES - ${FWCoreFoundation} -) diff --git a/src/macdeployqt/macdeployqt/main.cpp b/src/macdeployqt/macdeployqt/main.cpp deleted file mode 100644 index 145ee854a..000000000 --- a/src/macdeployqt/macdeployqt/main.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ -#include <QCoreApplication> -#include <QDir> -#include <QLibraryInfo> - -#include "../shared/shared.h" - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, 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() << " -executable=<path> : Let the given executable use the deployed frameworks too"; - qDebug() << " -qmldir=<path> : Scan for QML imports in the given path"; - qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations"; - qDebug() << " -always-overwrite : Copy files even if the target file exists"; - qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables"; - qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing"; - qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)"; - qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)"; - qDebug() << " -appstore-compliant : Skip deployment of components that use private API"; - qDebug() << " -libpath=<path> : Add the given path to the library search path"; - qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)"; - 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 accessibility, image formats, and text codec"; - qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; - qDebug() << ""; - qDebug() << "Qt plugins may use private API and will cause the app to be"; - qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning"; - qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant "; - qDebug() << "to skip these plugins. Currently two SQL plugins are known to"; - qDebug() << "be incompatible: qsqlodbc and qsqlpsql."; - qDebug() << ""; - qDebug() << "See the \"Deploying Applications on OS X\" topic in the"; - qDebug() << "documentation for more information about deployment on OS X."; - - return 1; - } - - appBundlePath = QDir::cleanPath(appBundlePath); - - if (!QDir(appBundlePath).exists()) { - qDebug() << "Error: Could not find app bundle" << appBundlePath; - return 1; - } - - bool plugins = true; - bool dmg = false; - QByteArray filesystem("HFS+"); - bool useDebugLibs = false; - extern bool runStripEnabled; - extern bool alwaysOwerwriteEnabled; - extern QStringList librarySearchPath; - QStringList additionalExecutables; - bool qmldirArgumentUsed = false; - QStringList qmlDirs; - QStringList qmlImportPaths; - extern bool runCodesign; - extern QString codesignIdentiy; - extern bool hardenedRuntime; - extern bool appstoreCompliant; - extern bool deployFramework; - extern bool secureTimestamp; - - 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(QByteArray("-executable"))) { - LogDebug() << "Argument found:" << argument; - int index = argument.indexOf('='); - if (index == -1) - LogError() << "Missing executable path"; - else - additionalExecutables << argument.mid(index+1); - } else if (argument.startsWith(QByteArray("-qmldir"))) { - LogDebug() << "Argument found:" << argument; - qmldirArgumentUsed = true; - int index = argument.indexOf('='); - if (index == -1) - LogError() << "Missing qml directory path"; - else - qmlDirs << argument.mid(index+1); - } else if (argument.startsWith(QByteArray("-qmlimport"))) { - LogDebug() << "Argument found:" << argument; - int index = argument.indexOf('='); - if (index == -1) - LogError() << "Missing qml import path"; - else - qmlImportPaths << argument.mid(index+1); - } else if (argument.startsWith(QByteArray("-libpath"))) { - LogDebug() << "Argument found:" << argument; - int index = argument.indexOf('='); - if (index == -1) - LogError() << "Missing library search path"; - else - librarySearchPath << argument.mid(index+1); - } 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(QByteArray("-sign-for-notarization"))) { - LogDebug() << "Argument found:" << argument; - int index = argument.indexOf("="); - if (index < 0 || index >= argument.size()) { - LogError() << "Missing code signing identity"; - } else { - runCodesign = true; - hardenedRuntime = true; - secureTimestamp = true; - codesignIdentiy = argument.mid(index+1); - } - } else if (argument.startsWith(QByteArray("-hardened-runtime"))) { - LogDebug() << "Argument found:" << argument; - hardenedRuntime = true; - } else if (argument.startsWith(QByteArray("-timestamp"))) { - LogDebug() << "Argument found:" << argument; - secureTimestamp = true; - } else if (argument == QByteArray("-appstore-compliant")) { - LogDebug() << "Argument found:" << argument; - appstoreCompliant = true; - - // Undocumented option, may not work as intended - } else if (argument == QByteArray("-deploy-framework")) { - LogDebug() << "Argument found:" << argument; - deployFramework = true; - - } else if (argument.startsWith(QByteArray("-fs"))) { - LogDebug() << "Argument found:" << argument; - int index = argument.indexOf('='); - if (index == -1) - LogError() << "Missing filesystem type"; - else - filesystem = argument.mid(index+1); - } else if (argument.startsWith("-")) { - LogError() << "Unknown argument" << argument << "\n"; - return 1; - } - } - - DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs); - - if (deploymentInfo.isDebug) - useDebugLibs = true; - - if (deployFramework && deploymentInfo.isFramework) - fixupFramework(appBundlePath); - - // Convenience: Look for .qml files in the current directory if no -qmldir specified. - if (qmlDirs.isEmpty()) { - QDir dir; - if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) { - qmlDirs += QStringLiteral("."); - } - } - - if (!qmlDirs.isEmpty()) { - bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths); - if (!ok && qmldirArgumentUsed) - return 1; // exit if the user explicitly asked for qml import deployment - - // Update deploymentInfo.deployedFrameworks - the QML imports - // may have brought in extra frameworks as dependencies. - deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath); - deploymentInfo.deployedFrameworks = - QSet<QString>(deploymentInfo.deployedFrameworks.begin(), - deploymentInfo.deployedFrameworks.end()).values(); - } - - // Handle plugins - if (plugins) { - // Set the plugins search directory - deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath); - - // Sanity checks - if (deploymentInfo.pluginPath.isEmpty()) { - LogError() << "Missing Qt plugins path\n"; - return 1; - } - - if (!QDir(deploymentInfo.pluginPath).exists()) { - LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n"; - return 1; - } - - // Deploy plugins - Q_ASSERT(!deploymentInfo.pluginPath.isEmpty()); - if (!deploymentInfo.pluginPath.isEmpty()) { - LogNormal(); - deployPlugins(appBundlePath, deploymentInfo, useDebugLibs); - createQtConf(appBundlePath); - } - } - - if (runStripEnabled) - stripAppBinary(appBundlePath); - - if (runCodesign) - codesign(codesignIdentiy, appBundlePath); - - if (dmg) { - LogNormal(); - createDiskImage(appBundlePath, filesystem); - } - - return 0; -} diff --git a/src/macdeployqt/shared/shared.cpp b/src/macdeployqt/shared/shared.cpp deleted file mode 100644 index 16608f72e..000000000 --- a/src/macdeployqt/shared/shared.cpp +++ /dev/null @@ -1,1603 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ -#include <QCoreApplication> -#include <QString> -#include <QStringList> -#include <QDebug> -#include <iostream> -#include <utility> -#include <QProcess> -#include <QDir> -#include <QSet> -#include <QStack> -#include <QDirIterator> -#include <QLibraryInfo> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QJsonValue> -#include <QRegularExpression> -#include "shared.h" - -#ifdef Q_OS_DARWIN -#include <CoreFoundation/CoreFoundation.h> -#endif - -bool runStripEnabled = true; -bool alwaysOwerwriteEnabled = false; -bool runCodesign = false; -QStringList librarySearchPath; -QString codesignIdentiy; -QString extraEntitlements; -bool hardenedRuntime = false; -bool secureTimestamp = false; -bool appstoreCompliant = false; -int logLevel = 1; -bool deployFramework = false; - -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 << "Framework Destination Directory (relative to bundle)" << info.frameworkDestinationDirectory << "\n"; - debug << "Binary Destination Directory (relative to bundle)" << info.binaryDestinationDirectory << "\n"; - - return debug; -} - -const QString bundleFrameworkDirectory = "Contents/Frameworks"; - -inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info) -{ - debug << "Application bundle path" << info.path << "\n"; - debug << "Binary path" << info.binaryPath << "\n"; - debug << "Additional libraries" << info.libraryPaths << "\n"; - return debug; -} - -bool copyFilePrintStatus(const QString &from, const QString &to) -{ - if (QFile::exists(to)) { - if (alwaysOwerwriteEnabled) { - QFile(to).remove(); - } else { - qDebug() << "File exists, skip copy:" << to; - return false; - } - } - - if (QFile::copy(from, to)) { - QFile dest(to); - dest.setPermissions(dest.permissions() | QFile::WriteOwner | QFile::WriteUser); - LogNormal() << " copied:" << from; - LogNormal() << " to" << to; - - // The source file might not have write permissions set. Set the - // write permission on the target file to make sure we can use - // install_name_tool on it later. - QFile toFile(to); - if (toFile.permissions() & QFile::WriteOwner) - return true; - - if (!toFile.setPermissions(toFile.permissions() | QFile::WriteOwner)) { - LogError() << "Failed to set u+w permissions on target file: " << to; - return false; - } - - return true; - } else { - LogError() << "file copy failed from" << from; - LogError() << " to" << to; - return false; - } -} - -bool linkFilePrintStatus(const QString &file, const QString &link) -{ - if (QFile::exists(link)) { - 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); -} - -OtoolInfo findDependencyInfo(const QString &binaryPath) -{ - OtoolInfo info; - info.binaryPath = binaryPath; - - LogDebug() << "Using otool:"; - LogDebug() << " inspecting" << binaryPath; - QProcess otool; - otool.start("otool", QStringList() << "-L" << binaryPath); - otool.waitForFinished(); - - if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) { - LogError() << otool.readAllStandardError(); - return info; - } - - static const QRegularExpression regexp(QStringLiteral( - "^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), " - "current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$")); - - QString output = otool.readAllStandardOutput(); - QStringList outputLines = output.split("\n", Qt::SkipEmptyParts); - if (outputLines.size() < 2) { - LogError() << "Could not parse otool output:" << output; - return info; - } - - outputLines.removeFirst(); // remove line containing the binary path - if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) { - const auto match = regexp.match(outputLines.first()); - if (match.hasMatch()) { - QString installname = match.captured(1); - if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) { - info.installName = installname; - info.compatibilityVersion = QVersionNumber::fromString(match.captured(2)); - info.currentVersion = QVersionNumber::fromString(match.captured(3)); - outputLines.removeFirst(); - } else { - info.installName = binaryPath; - } - } else { - LogError() << "Could not parse otool output line:" << outputLines.first(); - outputLines.removeFirst(); - } - } - - for (const QString &outputLine : outputLines) { - const auto match = regexp.match(outputLine); - if (match.hasMatch()) { - DylibInfo dylib; - dylib.binaryPath = match.captured(1); - dylib.compatibilityVersion = QVersionNumber::fromString(match.captured(2)); - dylib.currentVersion = QVersionNumber::fromString(match.captured(3)); - info.dependencies << dylib; - } else { - LogError() << "Could not parse otool output line:" << outputLine; - } - } - - return info; -} - -FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, 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") || trimmed.startsWith("@loader_path")) - return info; - - // Resolve rpath relative libraries. - if (trimmed.startsWith("@rpath/")) { - QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length()); - bool foundInsideBundle = false; - for (const QString &rpath : std::as_const(rpaths)) { - QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath); - // Skip paths already inside the bundle. - if (!appBundlePath.isEmpty()) { - if (QDir::isAbsolutePath(appBundlePath)) { - if (path.startsWith(QDir::cleanPath(appBundlePath) + "/")) { - foundInsideBundle = true; - continue; - } - } else { - if (path.startsWith(QDir::cleanPath(QDir::currentPath() + "/" + appBundlePath) + "/")) { - foundInsideBundle = true; - continue; - } - } - } - // Try again with substituted rpath. - FrameworkInfo resolvedInfo = parseOtoolLibraryLine(path, appBundlePath, rpaths, useDebugLibs); - if (!resolvedInfo.frameworkName.isEmpty() && QFile::exists(resolvedInfo.frameworkPath)) { - resolvedInfo.rpathUsed = rpath; - resolvedInfo.installName = trimmed; - return resolvedInfo; - } - } - if (!rpaths.isEmpty() && !foundInsideBundle) { - LogError() << "Cannot resolve rpath" << trimmed; - LogError() << " using" << rpaths; - } - return info; - } - - enum State {QtPath, FrameworkName, DylibName, Version, FrameworkBinary, 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")) { - info.frameworkDirectory += "/" + (qtPath + currentPart + "/").simplified(); - state = DylibName; - continue; - } else if (part < parts.count() && parts.at(part).endsWith(".framework")) { - info.frameworkDirectory += "/" + (qtPath + "lib/").simplified(); - 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. - QStringList partsCopy = parts; - partsCopy.removeLast(); - for (QString &path : librarySearchPath) { - if (!path.endsWith("/")) - path += '/'; - QString nameInPath = path + parts.join(QLatin1Char('/')); - if (QFile::exists(nameInPath)) { - info.frameworkDirectory = path + partsCopy.join(QLatin1Char('/')); - break; - } - } - if (currentPart.contains(".framework")) { - if (info.frameworkDirectory.isEmpty()) - info.frameworkDirectory = "/Library/Frameworks/" + partsCopy.join(QLatin1Char('/')); - if (!info.frameworkDirectory.endsWith("/")) - info.frameworkDirectory += "/"; - state = FrameworkName; - --part; - continue; - } else if (currentPart.contains(".dylib")) { - if (info.frameworkDirectory.isEmpty()) - info.frameworkDirectory = "/usr/lib/" + partsCopy.join(QLatin1Char('/')); - if (!info.frameworkDirectory.endsWith("/")) - info.frameworkDirectory += "/"; - state = DylibName; - --part; - continue; - } - } - qtPath += (currentPart + "/"); - - } if (state == FrameworkName) { - // remove ".framework" - name = currentPart; - name.chop(QString(".framework").length()); - info.isDylib = false; - info.frameworkName = currentPart; - state = Version; - ++part; - continue; - } if (state == DylibName) { - name = currentPart; - info.isDylib = true; - info.frameworkName = name; - info.binaryName = name.contains(suffix) ? name : name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.')); - info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName; - info.frameworkPath = info.frameworkDirectory + info.binaryName; - info.sourceFilePath = info.frameworkPath; - info.frameworkDestinationDirectory = bundleFrameworkDirectory + "/"; - info.binaryDestinationDirectory = info.frameworkDestinationDirectory; - 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.frameworkPath = info.frameworkDirectory + info.frameworkName; - info.frameworkDestinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName; - info.binaryDestinationDirectory = info.frameworkDestinationDirectory + "/" + info.binaryDirectory; - state = FrameworkBinary; - } else if (state == FrameworkBinary) { - info.binaryName = currentPart.contains(suffix) ? currentPart : currentPart + suffix; - info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName; - info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath; - info.sourceFilePath = info.frameworkPath + info.binaryPath; - state = End; - } else if (state == End) { - break; - } - } - - if (!info.sourceFilePath.isEmpty() && QFile::exists(info.sourceFilePath)) { - info.installName = findDependencyInfo(info.sourceFilePath).installName; - if (info.installName.startsWith("@rpath/")) - info.deployedInstallName = info.installName; - } - - return info; -} - -QString findAppBinary(const QString &appBundlePath) -{ - QString binaryPath; - -#ifdef Q_OS_DARWIN - CFStringRef bundlePath = appBundlePath.toCFString(); - CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, - kCFURLPOSIXPathStyle, true); - CFRelease(bundlePath); - CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL); - if (bundle) { - CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); - if (executableURL) { - CFURLRef absoluteExecutableURL = CFURLCopyAbsoluteURL(executableURL); - if (absoluteExecutableURL) { - CFStringRef executablePath = CFURLCopyFileSystemPath(absoluteExecutableURL, - kCFURLPOSIXPathStyle); - if (executablePath) { - binaryPath = QString::fromCFString(executablePath); - CFRelease(executablePath); - } - CFRelease(absoluteExecutableURL); - } - CFRelease(executableURL); - } - CFRelease(bundle); - } - CFRelease(bundleURL); -#endif - - if (QFile::exists(binaryPath)) - return binaryPath; - LogError() << "Could not find bundle binary for" << appBundlePath; - return QString(); -} - -QStringList findAppFrameworkNames(const QString &appBundlePath) -{ - QStringList frameworks; - - // populate the frameworks list with QtFoo.framework etc, - // as found in /Contents/Frameworks/ - QString searchPath = appBundlePath + "/Contents/Frameworks/"; - QDirIterator iter(searchPath, QStringList() << QString::fromLatin1("*.framework"), - QDir::Dirs | QDir::NoSymLinks); - while (iter.hasNext()) { - iter.next(); - frameworks << iter.fileInfo().fileName(); - } - - return frameworks; -} - -QStringList findAppFrameworkPaths(const QString &appBundlePath) -{ - QStringList frameworks; - QString searchPath = appBundlePath + "/Contents/Frameworks/"; - QDirIterator iter(searchPath, QStringList() << QString::fromLatin1("*.framework"), - QDir::Dirs | QDir::NoSymLinks); - while (iter.hasNext()) { - iter.next(); - frameworks << iter.fileInfo().filePath(); - } - - return frameworks; -} - -QStringList findAppLibraries(const QString &appBundlePath) -{ - QStringList result; - // dylibs - QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*.dylib"), - QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); - while (iter.hasNext()) { - iter.next(); - result << iter.fileInfo().filePath(); - } - return result; -} - -QStringList findAppBundleFiles(const QString &appBundlePath, bool absolutePath = false) -{ - QStringList result; - - QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*"), - QDir::Files, QDirIterator::Subdirectories); - - while (iter.hasNext()) { - iter.next(); - if (iter.fileInfo().isSymLink()) - continue; - result << (absolutePath ? iter.fileInfo().absoluteFilePath() : iter.fileInfo().filePath()); - } - - return result; -} - -QString findEntitlementsFile(const QString& path) -{ - QDirIterator iter(path, QStringList() << QString::fromLatin1("*.entitlements"), - QDir::Files, QDirIterator::Subdirectories); - - while (iter.hasNext()) { - iter.next(); - if (iter.fileInfo().isSymLink()) - continue; - - //return the first entitlements file - only one is used for signing anyway - return iter.fileInfo().absoluteFilePath(); - } - - return QString(); -} - -QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs) -{ - QList<FrameworkInfo> libraries; - for (const DylibInfo &dylibInfo : dependencies) { - FrameworkInfo info = parseOtoolLibraryLine(dylibInfo.binaryPath, appBundlePath, rpaths, useDebugLibs); - if (info.frameworkName.isEmpty() == false) { - LogDebug() << "Adding framework:"; - LogDebug() << info; - libraries.append(info); - } - } - return libraries; -} - -QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const QString &executablePath) -{ - if (path.startsWith("@")) { - if (path.startsWith(QStringLiteral("@executable_path/"))) { - // path relative to bundle executable dir - if (QDir::isAbsolutePath(executablePath)) { - return QDir::cleanPath(QFileInfo(executablePath).path() + path.mid(QStringLiteral("@executable_path").length())); - } else { - return QDir::cleanPath(QDir::currentPath() + "/" + - QFileInfo(executablePath).path() + path.mid(QStringLiteral("@executable_path").length())); - } - } else if (path.startsWith(QStringLiteral("@loader_path"))) { - // path relative to loader dir - if (QDir::isAbsolutePath(loaderPath)) { - return QDir::cleanPath(QFileInfo(loaderPath).path() + path.mid(QStringLiteral("@loader_path").length())); - } else { - return QDir::cleanPath(QDir::currentPath() + "/" + - QFileInfo(loaderPath).path() + path.mid(QStringLiteral("@loader_path").length())); - } - } else { - LogError() << "Unexpected prefix" << path; - } - } - return path; -} - -QList<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString()) -{ - QList<QString> rpaths; - - QProcess otool; - otool.start("otool", QStringList() << "-l" << path); - otool.waitForFinished(); - - if (otool.exitCode() != 0) { - LogError() << otool.readAllStandardError(); - } - - if (resolve && executablePath.isEmpty()) { - executablePath = path; - } - - QString output = otool.readAllStandardOutput(); - QStringList outputLines = output.split("\n"); - - for (auto i = outputLines.cbegin(), end = outputLines.cend(); i != end; ++i) { - if (i->contains("cmd LC_RPATH") && ++i != end && - i->contains("cmdsize") && ++i != end) { - const QString &rpathCmd = *i; - int pathStart = rpathCmd.indexOf("path "); - int pathEnd = rpathCmd.indexOf(" ("); - if (pathStart >= 0 && pathEnd >= 0 && pathStart < pathEnd) { - QString rpath = rpathCmd.mid(pathStart + 5, pathEnd - pathStart - 5); - if (resolve) { - rpaths << resolveDyldPrefix(rpath, path, executablePath); - } else { - rpaths << rpath; - } - } - } - } - - return rpaths; -} - -QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs) -{ - const OtoolInfo info = findDependencyInfo(path); - QList<QString> allRPaths = rpaths + getBinaryRPaths(path); - allRPaths.removeDuplicates(); - return getQtFrameworks(info.dependencies, appBundlePath, allRPaths, useDebugLibs); -} - -QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs) -{ - QList<FrameworkInfo> result; - QSet<QString> existing; - for (const QString &path : paths) { - for (const FrameworkInfo &info : getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) { - if (!existing.contains(info.frameworkPath)) { // avoid duplicates - existing.insert(info.frameworkPath); - result << info; - } - } - } - return result; -} - -QStringList getBinaryDependencies(const QString executablePath, - const QString &path, - const QList<QString> &additionalBinariesContainingRpaths) -{ - QStringList binaries; - - const auto dependencies = findDependencyInfo(path).dependencies; - - bool rpathsLoaded = false; - QList<QString> rpaths; - - // return bundle-local dependencies. (those starting with @executable_path) - for (const DylibInfo &info : dependencies) { - QString trimmedLine = info.binaryPath; - if (trimmedLine.startsWith("@executable_path/")) { - QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_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) - rpaths += getBinaryRPaths(binaryPath, true); - rpaths.removeDuplicates(); - rpathsLoaded = true; - } - bool resolved = false; - for (const QString &rpath : std::as_const(rpaths)) { - QString binary = QDir::cleanPath(rpath + "/" + trimmedLine.mid(QStringLiteral("@rpath/").length())); - LogDebug() << "Checking for" << binary; - if (QFile::exists(binary)) { - binaries.append(binary); - resolved = true; - break; - } - } - if (!resolved && !rpaths.isEmpty()) { - LogError() << "Cannot resolve rpath" << trimmedLine; - LogError() << " using" << rpaths; - } - } - } - - return binaries; -} - -// copies everything _inside_ sourcePath to destinationPath -bool recursiveCopy(const QString &sourcePath, const QString &destinationPath) -{ - if (!QDir(sourcePath).exists()) - return false; - QDir().mkpath(destinationPath); - - LogNormal() << "copy:" << sourcePath << destinationPath; - - QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot); - for (const 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); - for (const QString &dir : subdirs) { - recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir); - } - return true; -} - -void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &rpaths, const QString &sourcePath, const QString &destinationPath) -{ - QDir().mkpath(destinationPath); - - LogNormal() << "copy:" << sourcePath << destinationPath; - const bool isDwarfPath = sourcePath.endsWith("DWARF"); - - QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot); - for (const QString &file : files) { - const QString fileSourcePath = sourcePath + QLatin1Char('/') + file; - - if (file.endsWith("_debug.dylib")) { - continue; // Skip debug versions - } else if (!isDwarfPath && 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("/")) - appBundlePath.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; - QList<FrameworkInfo> frameworks = getQtFrameworks(fileDestinationPath, appBundlePath, rpaths, useDebugLibs); - deployQtFrameworks(frameworks, appBundlePath, QStringList(fileDestinationPath), useDebugLibs, useLoaderPath); - } - } else { - QString fileDestinationPath = destinationPath + QLatin1Char('/') + file; - copyFilePrintStatus(fileSourcePath, fileDestinationPath); - } - } - - QStringList subdirs = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &dir : subdirs) { - recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + QLatin1Char('/') + dir, destinationPath + QLatin1Char('/') + dir); - } -} - -QString copyDylib(const FrameworkInfo &framework, const QString path) -{ - if (!QFile::exists(framework.sourceFilePath)) { - LogError() << "no file at" << framework.sourceFilePath; - return QString(); - } - - // 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(); - } - - // Return if the dylib has already been deployed - if (QFileInfo::exists(dylibDestinationBinaryPath) && !alwaysOwerwriteEnabled) - return dylibDestinationBinaryPath; - - // Copy dylib binary - copyFilePrintStatus(framework.sourceFilePath, dylibDestinationBinaryPath); - return dylibDestinationBinaryPath; -} - -QString copyFramework(const FrameworkInfo &framework, const QString path) -{ - if (!QFile::exists(framework.sourceFilePath)) { - LogError() << "no file at" << framework.sourceFilePath; - return QString(); - } - - // 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; - - // 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(); - } - - // 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 assumptions about the framework as possible while at - // the same time producing a codesign-compatible framework. - - // Copy framework binary - copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath); - - // Copy Resources/, Libraries/ and Helpers/ - const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; - const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources"; - recursiveCopy(resourcesSourcePath, resourcesDestianationPath); - const QString librariesSourcePath = framework.frameworkPath + "/Libraries"; - const QString librariesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Libraries"; - bool createdLibraries = recursiveCopy(librariesSourcePath, librariesDestianationPath); - const QString helpersSourcePath = framework.frameworkPath + "/Helpers"; - const QString helpersDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Helpers"; - bool createdHelpers = recursiveCopy(helpersSourcePath, helpersDestianationPath); - - // 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"); - if (createdLibraries) - linkFilePrintStatus("Versions/Current/Libraries", frameworkDestinationDirectory + "/Libraries"); - if (createdHelpers) - linkFilePrintStatus("Versions/Current/Helpers", frameworkDestinationDirectory + "/Helpers"); - 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::exists(legacyInfoPlistPath)) { - copyFilePrintStatus(legacyInfoPlistPath, correctInfoPlistPath); - patch_debugInInfoPlist(correctInfoPlistPath); - } - return frameworkDestinationBinaryPath; -} - -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 &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath) -{ - const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath(); - for (const QString &binary : binaryPaths) { - QString deployedInstallName; - if (useLoaderPath) { - deployedInstallName = QLatin1String("@loader_path/") - + QFileInfo(binary).absoluteDir().relativeFilePath(absBundlePath + QLatin1Char('/') + framework.binaryDestinationDirectory + QLatin1Char('/') + 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(); - if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) { - changeInstallName(canonicalInstallName, deployedInstallName, binary); - } - } -} - -void addRPath(const QString &rpath, const QString &binaryPath) -{ - runInstallNameTool(QStringList() << "-add_rpath" << rpath << binaryPath); -} - -void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QString &binaryPath, bool useLoaderPath) -{ - const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath() - + QLatin1String("/Contents/Frameworks"); - const QString relativeFrameworkPath = QFileInfo(binaryPath).absoluteDir().relativeFilePath(absFrameworksPath); - const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath; - bool rpathToFrameworksFound = false; - QStringList args; - QList<QString> binaryRPaths = getBinaryRPaths(binaryPath, false); - for (const QString &rpath : std::as_const(binaryRPaths)) { - if (rpath == "@executable_path/../Frameworks" || - rpath == loaderPathToFrameworks) { - rpathToFrameworksFound = true; - continue; - } - if (rpaths.contains(resolveDyldPrefix(rpath, binaryPath, binaryPath))) { - args << "-delete_rpath" << rpath; - } - } - if (!args.length()) { - return; - } - if (!rpathToFrameworksFound) { - if (!useLoaderPath) { - args << "-add_rpath" << "@executable_path/../Frameworks"; - } else { - args << "-add_rpath" << loaderPathToFrameworks; - } - } - LogDebug() << "Using install_name_tool:"; - LogDebug() << " change rpaths in" << binaryPath; - LogDebug() << " using" << args; - runInstallNameTool(QStringList() << args << binaryPath); -} - -void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath) -{ - for (const QString &binary : binaryPaths) { - deployRPaths(bundlePath, rpaths, binary, useLoaderPath); - } -} - -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(); - } -} - -void stripAppBinary(const QString &bundlePath) -{ - runStrip(findAppBinary(bundlePath)); -} - -bool DeploymentInfo::containsModule(const QString &module, const QString &libInFix) const -{ - // Check for framework first - if (deployedFrameworks.contains(QLatin1String("Qt") + module + libInFix + - QLatin1String(".framework"))) { - return true; - } - // Check for dylib - const QRegularExpression dylibRegExp(QLatin1String("libQt[0-9]+") + module + - libInFix + QLatin1String(".[0-9]+.dylib")); - return deployedFrameworks.filter(dylibRegExp).size() > 0; -} - -/* - 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 QStringList &binaryPaths, bool useDebugLibs, - bool useLoaderPath) -{ - LogNormal(); - LogNormal() << "Deploying Qt frameworks found inside:" << binaryPaths; - QStringList copiedFrameworks; - DeploymentInfo deploymentInfo; - deploymentInfo.useLoaderPath = useLoaderPath; - deploymentInfo.isFramework = bundlePath.contains(".framework"); - deploymentInfo.isDebug = false; - QList<QString> rpathsUsed; - - while (frameworks.isEmpty() == false) { - const FrameworkInfo framework = frameworks.takeFirst(); - copiedFrameworks.append(framework.frameworkName); - - // If a single dependency has the _debug suffix, we treat that as - // the whole deployment being a debug deployment, including deploying - // the debug version of plugins. - if (framework.isDebugLibrary()) - deploymentInfo.isDebug = true; - - if (deploymentInfo.qtPath.isNull()) - deploymentInfo.qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath); - - if (framework.frameworkDirectory.startsWith(bundlePath)) { - LogError() << framework.frameworkName << "already deployed, skipping."; - continue; - } - - if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) { - rpathsUsed.append(framework.rpathUsed); - } - - // Copy the framework/dylib to the app bundle. - const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath) - : copyFramework(framework, bundlePath); - - // Install_name_tool the new id into the binaries - changeInstallName(bundlePath, framework, binaryPaths, useLoaderPath); - - // Skip the rest if already was deployed. - if (deployedBinaryPath.isNull()) - continue; - - runStrip(deployedBinaryPath); - - // Install_name_tool it a new id. - if (!framework.rpathUsed.length()) { - changeIdentification(framework.deployedInstallName, deployedBinaryPath); - } - - // Check for framework dependencies - QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs); - - for (const FrameworkInfo &dependency : dependencies) { - if (dependency.rpathUsed.isEmpty()) { - changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath); - } else { - rpathsUsed.append(dependency.rpathUsed); - } - - // Deploy framework if necessary. - if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) { - frameworks.append(dependency); - } - } - } - deploymentInfo.deployedFrameworks = copiedFrameworks; - deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath); - deploymentInfo.rpathsUsed += rpathsUsed; - deploymentInfo.rpathsUsed.removeDuplicates(); - return deploymentInfo; -} - -DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs) -{ - ApplicationBundleInfo applicationBundle; - applicationBundle.path = appBundlePath; - applicationBundle.binaryPath = findAppBinary(appBundlePath); - applicationBundle.libraryPaths = findAppLibraries(appBundlePath); - QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths - << additionalExecutables; - - QList<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true); - allLibraryPaths.append(QLibraryInfo::path(QLibraryInfo::LibrariesPath)); - allLibraryPaths.removeDuplicates(); - - QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs); - if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) { - 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, allBinaryPaths, useDebugLibs, !additionalExecutables.isEmpty()); - } -} - -QString getLibInfix(const QStringList &deployedFrameworks) -{ - QString libInfix; - for (const QString &framework : deployedFrameworks) { - if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework")) && - !framework.contains(QStringLiteral("5Compat"))) { - Q_ASSERT(framework.length() >= 16); - // 16 == "QtCore" + ".framework" - const int lengthOfLibInfix = framework.length() - 16; - if (lengthOfLibInfix) - libInfix = framework.mid(6, lengthOfLibInfix); - break; - } - } - return libInfix; -} - -void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, - const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs) -{ - LogNormal() << "Deploying plugins from" << pluginSourcePath; - - if (!pluginSourcePath.contains(deploymentInfo.pluginPath)) - return; - - // Plugin white list: - QStringList pluginList; - - 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) - .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); - } - }; - - // Platform plugin: - addPlugins(QStringLiteral("platforms"), [](const QString &lib) { - // Ignore minimal and offscreen platform plugins - if (!lib.contains(QStringLiteral("cocoa"))) - return false; - return true; - }); - - // Cocoa print support - addPlugins(QStringLiteral("printsupport")); - - // Styles - addPlugins(QStringLiteral("styles")); - - // Check if Qt was configured with -libinfix - const QString libInfix = getLibInfix(deploymentInfo.deployedFrameworks); - - // Network - if (deploymentInfo.containsModule("Network", libInfix)) - addPlugins(QStringLiteral("tls")); - - // All image formats (svg if QtSvg is used) - const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix); - addPlugins(QStringLiteral("imageformats"), [usesSvg](const QString &lib) { - if (lib.contains(QStringLiteral("qsvg")) && !usesSvg) - return false; - return true; - }); - - addPlugins(QStringLiteral("iconengines")); - - // Platforminputcontext plugins if QtGui is in use - if (deploymentInfo.containsModule("Gui", libInfix)) { - addPlugins(QStringLiteral("platforminputcontexts"), [&addPlugins](const QString &lib) { - // Deploy the virtual keyboard plugins if we have deployed virtualkeyboard - if (lib.startsWith(QStringLiteral("libqtvirtualkeyboard"))) - addPlugins(QStringLiteral("virtualkeyboard")); - return true; - }); - } - - // Sql plugins if QtSql is in use - if (deploymentInfo.containsModule("Sql", libInfix)) { - addPlugins(QStringLiteral("sqldrivers"), [](const QString &lib) { - if (lib.startsWith(QStringLiteral("libqsqlodbc")) || lib.startsWith(QStringLiteral("libqsqlpsql"))) { - LogWarning() << "Plugin" << lib << "uses private API and is not Mac App store compliant."; - if (appstoreCompliant) { - LogWarning() << "Skip plugin" << lib; - return false; - } - } - return true; - }); - } - - // WebView plugins if QtWebView is in use - if (deploymentInfo.containsModule("WebView", libInfix)) { - addPlugins(QStringLiteral("webview"), [](const QString &lib) { - if (lib.startsWith(QStringLiteral("libqtwebview_webengine"))) { - LogWarning() << "Plugin" << lib << "uses QtWebEngine and is not Mac App store compliant."; - if (appstoreCompliant) { - LogWarning() << "Skip plugin" << lib; - return false; - } - } - return true; - }); - } - - static const std::map<QString, std::vector<QString>> map { - {QStringLiteral("Multimedia"), {QStringLiteral("mediaservice"), QStringLiteral("audio")}}, - {QStringLiteral("3DRender"), {QStringLiteral("sceneparsers"), QStringLiteral("geometryloaders"), QStringLiteral("renderers")}}, - {QStringLiteral("3DQuickRender"), {QStringLiteral("renderplugins")}}, - {QStringLiteral("Positioning"), {QStringLiteral("position")}}, - {QStringLiteral("Location"), {QStringLiteral("geoservices")}}, - {QStringLiteral("TextToSpeech"), {QStringLiteral("texttospeech")}} - }; - - for (const auto &it : map) { - if (deploymentInfo.containsModule(it.first, libInfix)) { - for (const auto &pluginType : it.second) { - addPlugins(pluginType); - } - } - } - - for (const QString &plugin : pluginList) { - QString sourcePath = pluginSourcePath + "/" + plugin; - const QString destinationPath = pluginDestinationPath + "/" + plugin; - QDir dir; - dir.mkpath(QFileInfo(destinationPath).path()); - - if (copyFilePrintStatus(sourcePath, destinationPath)) { - runStrip(destinationPath); - QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs); - deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath); - } - } -} - -void createQtConf(const QString &appBundlePath) -{ - // Set Plugins and imports paths. These are relative to App.app/Contents. - QByteArray contents = "[Paths]\n" - "Plugins = PlugIns\n" - "QmlImports = Resources/qml\n"; - - QString filePath = appBundlePath + "/Contents/Resources/"; - QString fileName = filePath + "qt.conf"; - - QDir().mkpath(filePath); - - QFile qtconf(fileName); - if (qtconf.exists() && !alwaysOwerwriteEnabled) { - 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 deployQmlImport(const QString &appBundlePath, const QList<QString> &rpaths, 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, rpaths, importSourcePath, importDestinationPath); -} - -static bool importLessThan(const QVariant &v1, const QVariant &v2) -{ - QVariantMap import1 = v1.toMap(); - QVariantMap import2 = v2.toMap(); - QString path1 = import1["path"].toString(); - QString path2 = import2["path"].toString(); - return path1 < path2; -} - -// Scan qml files in qmldirs for import statements, deploy used imports from QmlImportsPath to Contents/Resources/qml. -bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths) -{ - LogNormal() << ""; - LogNormal() << "Deploying QML imports "; - LogNormal() << "Application QML file path(s) is" << qmlDirs; - LogNormal() << "QML module search path(s) is" << qmlImportPaths; - - // Use qmlimportscanner from QLibraryInfo::LibraryExecutablesPath - QString qmlImportScannerPath = - QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) - + "/qmlimportscanner"); - - // Fallback: Look relative to the macdeployqt binary - if (!QFile::exists(qmlImportScannerPath)) - qmlImportScannerPath = QCoreApplication::applicationDirPath() + "/qmlimportscanner"; - - // Verify that we found a qmlimportscanner binary - if (!QFile::exists(qmlImportScannerPath)) { - LogError() << "qmlimportscanner not found at" << qmlImportScannerPath; - LogError() << "Rebuild qtdeclarative/tools/qmlimportscanner"; - return false; - } - - // build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml" - // ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed) - QStringList argumentList; - for (const QString &qmlDir : qmlDirs) { - argumentList.append("-rootPath"); - argumentList.append(qmlDir); - } - for (const QString &importPath : qmlImportPaths) - argumentList << "-importPath" << importPath; - QString qmlImportsPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); - argumentList.append( "-importPath"); - argumentList.append(qmlImportsPath); - - // run qmlimportscanner - QProcess qmlImportScanner; - qmlImportScanner.start(qmlImportScannerPath, argumentList); - if (!qmlImportScanner.waitForStarted()) { - LogError() << "Could not start qmlimpoortscanner. Process error is" << qmlImportScanner.errorString(); - return false; - } - qmlImportScanner.waitForFinished(-1); - - // log qmlimportscanner errors - qmlImportScanner.setReadChannel(QProcess::StandardError); - QByteArray errors = qmlImportScanner.readAll(); - if (!errors.isEmpty()) { - LogWarning() << "QML file parse error (deployment will continue):"; - LogWarning() << errors; - } - - // parse qmlimportscanner json - qmlImportScanner.setReadChannel(QProcess::StandardOutput); - QByteArray json = qmlImportScanner.readAll(); - QJsonDocument doc = QJsonDocument::fromJson(json); - if (!doc.isArray()) { - LogError() << "qmlimportscanner output error. Expected json array, got:"; - LogError() << json; - return false; - } - - // sort imports to deploy a module before its sub-modules (otherwise - // deployQmlImports can consider the module deployed if it has already - // deployed one of its sub-module) - QVariantList array = doc.array().toVariantList(); - std::sort(array.begin(), array.end(), importLessThan); - - // deploy each import - for (const QVariant &importValue : array) { - QVariantMap import = importValue.toMap(); - QString name = import["name"].toString(); - QString path = import["path"].toString(); - QString type = import["type"].toString(); - - LogNormal() << "Deploying QML import" << name; - - // Skip imports with missing info - path will be empty if the import is not found. - if (name.isEmpty() || path.isEmpty()) { - LogNormal() << " Skip import: name or path is empty"; - LogNormal() << ""; - continue; - } - - // Deploy module imports only, skip directory (local/remote) and js imports. These - // should be deployed as a part of the application build. - if (type != QStringLiteral("module")) { - LogNormal() << " Skip non-module import"; - LogNormal() << ""; - continue; - } - - // Create the destination path from the name - // and version (grabbed from the source path) - // ### let qmlimportscanner provide this. - name.replace(QLatin1Char('.'), QLatin1Char('/')); - int secondTolast = path.length() - 2; - QString version = path.mid(secondTolast); - if (version.startsWith(QLatin1Char('.'))) - name.append(version); - - deployQmlImport(appBundlePath, deploymentInfo.rpathsUsed, path, name); - LogNormal() << ""; - } - return true; -} - -void codesignFile(const QString &identity, const QString &filePath) -{ - if (!runCodesign) - return; - - QString codeSignLogMessage = "codesign"; - if (hardenedRuntime) - codeSignLogMessage += ", enable hardened runtime"; - if (secureTimestamp) - codeSignLogMessage += ", include secure timestamp"; - LogNormal() << codeSignLogMessage << filePath; - - QStringList codeSignOptions = { "--preserve-metadata=identifier,entitlements", "--force", "-s", - identity, filePath }; - if (hardenedRuntime) - codeSignOptions << "-o" << "runtime"; - - if (secureTimestamp) - codeSignOptions << "--timestamp"; - - if (!extraEntitlements.isEmpty()) - codeSignOptions << "--entitlements" << extraEntitlements; - - QProcess codesign; - codesign.start("codesign", codeSignOptions); - codesign.waitForFinished(-1); - - QByteArray err = codesign.readAllStandardError(); - if (codesign.exitCode() > 0) { - LogError() << "Codesign signing error:"; - LogError() << err; - } else if (!err.isEmpty()) { - LogDebug() << err; - } -} - -QSet<QString> codesignBundle(const QString &identity, - const QString &appBundlePath, - QList<QString> additionalBinariesContainingRpaths) -{ - // 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> pendingBinariesSet; - QSet<QString> signedBinaries; - - // Create the root code-binary set. This set consists of the application - // executable(s) and the plugins. - QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath(); - QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/"; - QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files); - for (const QString &binary : foundRootBinaries) { - QString binaryPath = rootBinariesPath + binary; - pendingBinaries.push(binaryPath); - pendingBinariesSet.insert(binaryPath); - additionalBinariesContainingRpaths.append(binaryPath); - } - - bool getAbsoltuePath = true; - QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath); - for (const QString &binary : foundPluginBinaries) { - pendingBinaries.push(binary); - pendingBinariesSet.insert(binary); - } - - // Add frameworks for processing. - QStringList frameworkPaths = findAppFrameworkPaths(appBundlePath); - for (const QString &frameworkPath : frameworkPaths) { - - // Prioritise first to sign any additional inner bundles found in the Helpers folder (e.g - // used by QtWebEngine). - QDirIterator helpersIterator(frameworkPath, QStringList() << QString::fromLatin1("Helpers"), QDir::Dirs | QDir::NoSymLinks, QDirIterator::Subdirectories); - while (helpersIterator.hasNext()) { - helpersIterator.next(); - QString helpersPath = helpersIterator.filePath(); - QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs); - for (const QString &innerBundleName : innerBundleNames) - signedBinaries += codesignBundle(identity, - helpersPath + "/" + innerBundleName, - additionalBinariesContainingRpaths); - } - - // Also make sure to sign any libraries that will not be found by otool because they - // are not linked and won't be seen as a dependency. - QDirIterator librariesIterator(frameworkPath, QStringList() << QString::fromLatin1("Libraries"), QDir::Dirs | QDir::NoSymLinks, QDirIterator::Subdirectories); - while (librariesIterator.hasNext()) { - librariesIterator.next(); - QString librariesPath = librariesIterator.filePath(); - QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath); - for (const QString &binary : bundleFiles) { - pendingBinaries.push(binary); - pendingBinariesSet.insert(binary); - } - } - } - - // Sign all binaries; 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, - additionalBinariesContainingRpaths); - dependencies = QSet<QString>(dependencies.begin(), dependencies.end()) - .subtract(signedBinaries) - .subtract(pendingBinariesSet) - .values(); - - if (!dependencies.isEmpty()) { - pendingBinaries.push(binary); - pendingBinariesSet.insert(binary); - int dependenciesSkipped = 0; - for (const QString &dependency : std::as_const(dependencies)) { - // Skip dependencies that are outside the current app bundle, because this might - // cause a codesign error if the current bundle is part of the dependency (e.g. - // a bundle is part of a framework helper, and depends on that framework). - // The dependencies will be taken care of after the current bundle is signed. - if (!dependency.startsWith(appBundleAbsolutePath)) { - ++dependenciesSkipped; - LogNormal() << "Skipping outside dependency: " << dependency; - continue; - } - pendingBinaries.push(dependency); - pendingBinariesSet.insert(dependency); - } - - // If all dependencies were skipped, make sure the binary is actually signed, instead - // of going into an infinite loop. - if (dependenciesSkipped == dependencies.size()) { - pendingBinaries.pop(); - } else { - continue; - } - } - - // Look for an entitlements file in the bundle to include when signing - extraEntitlements = findEntitlementsFile(appBundleAbsolutePath + "/Contents/Resources/"); - - // All dependencies are signed, now sign this binary. - codesignFile(identity, binary); - signedBinaries.insert(binary); - pendingBinariesSet.remove(binary); - } - - LogNormal() << "Finished codesigning " << appBundlePath << "with identity" << identity; - - // 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; - } - - return signedBinaries; -} - -void codesign(const QString &identity, const QString &appBundlePath) { - codesignBundle(identity, appBundlePath, QList<QString>()); -} - -void createDiskImage(const QString &appBundlePath, const QString &filesystemType) -{ - QString appBaseName = appBundlePath; - appBaseName.chop(4); // remove ".app" from end - - QString dmgName = appBaseName + ".dmg"; - - QFile dmg(dmgName); - - if (dmg.exists() && alwaysOwerwriteEnabled) - dmg.remove(); - - if (dmg.exists()) { - LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); - } else { - LogNormal() << "Creating disk image (.dmg) for" << appBundlePath; - } - - LogNormal() << "Image will use" << filesystemType; - - // More dmg options can be found in the hdiutil man page. - QStringList options = QStringList() - << "create" << dmgName - << "-srcfolder" << appBundlePath - << "-format" << "UDZO" - << "-fs" << filesystemType - << "-volname" << appBaseName; - - QProcess hdutil; - hdutil.start("hdiutil", options); - hdutil.waitForFinished(-1); - if (hdutil.exitCode() != 0) { - LogError() << "Bundle creation error:" << hdutil.readAllStandardError(); - } -} - -void fixupFramework(const QString &frameworkName) -{ - // Expected framework name looks like "Foo.framework" - QStringList parts = frameworkName.split("."); - if (parts.count() < 2) { - LogError() << "fixupFramework: Unexpected framework name" << frameworkName; - return; - } - - // Assume framework binary path is Foo.framework/Foo - QString frameworkBinary = frameworkName + QStringLiteral("/") + parts[0]; - - // Xcode expects to find Foo.framework/Versions/A when code - // signing, while qmake typically generates numeric versions. - // Create symlink to the actual version in the framework. - linkFilePrintStatus("Current", frameworkName + "/Versions/A"); - - // Set up @rpath structure. - changeIdentification("@rpath/" + frameworkBinary, frameworkBinary); - addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary); -} diff --git a/src/macdeployqt/shared/shared.h b/src/macdeployqt/shared/shared.h deleted file mode 100644 index 48ea24b11..000000000 --- a/src/macdeployqt/shared/shared.h +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ -#ifndef MAC_DEPLOMYMENT_SHARED_H -#define MAC_DEPLOMYMENT_SHARED_H - -#include <QString> -#include <QStringList> -#include <QDebug> -#include <QSet> -#include <QVersionNumber> - -extern int logLevel; -#define LogError() if (logLevel < 0) {} 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: - bool isDylib; - QString frameworkDirectory; - QString frameworkName; - QString frameworkPath; - QString binaryDirectory; - QString binaryName; - QString binaryPath; - QString rpathUsed; - QString version; - QString installName; - QString deployedInstallName; - QString sourceFilePath; - QString frameworkDestinationDirectory; - QString binaryDestinationDirectory; - - bool isDebugLibrary() const - { - return binaryName.endsWith(QStringLiteral("_debug")); - } -}; - -class DylibInfo -{ -public: - QString binaryPath; - QVersionNumber currentVersion; - QVersionNumber compatibilityVersion; -}; - -class OtoolInfo -{ -public: - QString installName; - QString binaryPath; - QVersionNumber currentVersion; - QVersionNumber compatibilityVersion; - QList<DylibInfo> dependencies; -}; - -bool operator==(const FrameworkInfo &a, const FrameworkInfo &b); -QDebug operator<<(QDebug debug, const FrameworkInfo &info); - -class ApplicationBundleInfo -{ - public: - QString path; - QString binaryPath; - QStringList libraryPaths; -}; - -class DeploymentInfo -{ -public: - QString qtPath; - QString pluginPath; - QStringList deployedFrameworks; - QList<QString> rpathsUsed; - bool useLoaderPath; - bool isFramework; - bool isDebug; - - bool containsModule(const QString &module, const QString &libInFix) const; -}; - -inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info); - -OtoolInfo findDependencyInfo(const QString &binaryPath); -FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs); -QString findAppBinary(const QString &appBundlePath); -QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs); -QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs); -QString copyFramework(const FrameworkInfo &framework, const QString path); -DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs); -DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath); -void createQtConf(const QString &appBundlePath); -void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs); -bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths); -void changeIdentification(const QString &id, const QString &binaryPath); -void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath); -void runStrip(const QString &binaryPath); -void stripAppBinary(const QString &bundlePath); -QString findAppBinary(const QString &appBundlePath); -QStringList findAppFrameworkNames(const QString &appBundlePath); -QStringList findAppFrameworkPaths(const QString &appBundlePath); -void codesignFile(const QString &identity, const QString &filePath); -QSet<QString> codesignBundle(const QString &identity, - const QString &appBundlePath, - QList<QString> additionalBinariesContainingRpaths); -void codesign(const QString &identity, const QString &appBundlePath); -void createDiskImage(const QString &appBundlePath, const QString &filesystemType); -void fixupFramework(const QString &appBundlePath); - - -#endif diff --git a/src/shared/winutils/elfreader.cpp b/src/shared/winutils/elfreader.cpp deleted file mode 100644 index f375f5841..000000000 --- a/src/shared/winutils/elfreader.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "elfreader.h" - -#include <QDir> - -QT_BEGIN_NAMESPACE - -/* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils), - * extended by the dependencies() function to read out the dependencies of a dynamic executable. */ - -quint16 getHalfWord(const unsigned char *&s, const ElfData &context) -{ - quint16 res; - if (context.endian == Elf_ELFDATA2MSB) - res = qFromBigEndian<quint16>(s); - else - res = qFromLittleEndian<quint16>(s); - s += 2; - return res; -} - -quint32 getWord(const unsigned char *&s, const ElfData &context) -{ - quint32 res; - if (context.endian == Elf_ELFDATA2MSB) - res = qFromBigEndian<quint32>(s); - else - res = qFromLittleEndian<quint32>(s); - s += 4; - return res; -} - -quint64 getAddress(const unsigned char *&s, const ElfData &context) -{ - quint64 res; - if (context.elfclass == Elf_ELFCLASS32) { - if (context.endian == Elf_ELFDATA2MSB) - res = qFromBigEndian<quint32>(s); - else - res = qFromLittleEndian<quint32>(s); - s += 4; - } else { - if (context.endian == Elf_ELFDATA2MSB) - res = qFromBigEndian<quint64>(s); - else - res = qFromLittleEndian<quint64>(s); - s += 8; - } - return res; -} - -quint64 getOffset(const unsigned char *&s, const ElfData &context) -{ - return getAddress(s, context); -} - -static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context) -{ - sh->index = getWord(s, context); - sh->type = getWord(s, context); - sh->flags = quint32(getOffset(s, context)); - sh->addr = getAddress(s, context); - sh->offset = getOffset(s, context); - sh->size = getOffset(s, context); -} - -static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context) -{ - sh->type = getWord(s, context); - sh->offset = getOffset(s, context); - /* p_vaddr = */ getAddress(s, context); - /* p_paddr = */ getAddress(s, context); - sh->filesz = getWord(s, context); - sh->memsz = getWord(s, context); -} - -class ElfMapper -{ -public: - ElfMapper(const ElfReader *reader) : file(reader->m_binary) {} - - bool map() - { - if (!file.open(QIODevice::ReadOnly)) - return false; - - fdlen = quint64(file.size()); - ustart = file.map(0, qint64(fdlen)); - if (ustart == 0) { - // Try reading the data into memory instead. - raw = file.readAll(); - start = raw.constData(); - fdlen = quint64(raw.size()); - } - return true; - } - -public: - QFile file; - QByteArray raw; - union { const char *start; const uchar *ustart; }; - quint64 fdlen; -}; - -ElfReader::ElfReader(const QString &binary) - : m_binary(binary) -{ -} - -ElfData ElfReader::readHeaders() -{ - readIt(); - return m_elfData; -} - -static inline QString msgInvalidElfObject(const QString &binary, const QString &why) -{ - return QStringLiteral("'%1' is an invalid ELF object (%2)") - .arg(QDir::toNativeSeparators(binary), why); -} - -ElfReader::Result ElfReader::readIt() -{ - if (!m_elfData.sectionHeaders.isEmpty()) - return Ok; - if (!m_elfData.programHeaders.isEmpty()) - return Ok; - - ElfMapper mapper(this); - if (!mapper.map()) - return Corrupt; - - const quint64 fdlen = mapper.fdlen; - - if (fdlen < 64) { - m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary)); - return NotElf; - } - - if (strncmp(mapper.start, "\177ELF", 4) != 0) { - m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary)); - return NotElf; - } - - // 32 or 64 bit - m_elfData.elfclass = ElfClass(mapper.start[4]); - const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64; - if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd cpu architecture")); - return Corrupt; - } - - // int bits = (data[4] << 5); - // If you remove this check to read ELF objects of a different arch, - // please make sure you modify the typedefs - // to match the _plugin_ architecture. - // if ((sizeof(void*) == 4 && bits != 32) - // || (sizeof(void*) == 8 && bits != 64)) { - // if (errorString) - // *errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)") - // .arg(m_binary).arg(QLatin1String("wrong cpu architecture")); - // return Corrupt; - // } - - // Read Endianhness. - m_elfData.endian = ElfEndian(mapper.ustart[5]); - if (m_elfData.endian != Elf_ELFDATA2LSB && m_elfData.endian != Elf_ELFDATA2MSB) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd endianness")); - return Corrupt; - } - - const uchar *data = mapper.ustart + 16; // e_ident - m_elfData.elftype = ElfType(getHalfWord(data, m_elfData)); - m_elfData.elfmachine = ElfMachine(getHalfWord(data, m_elfData)); - /* e_version = */ getWord(data, m_elfData); - m_elfData.entryPoint = getAddress(data, m_elfData); - - quint64 e_phoff = getOffset(data, m_elfData); - quint64 e_shoff = getOffset(data, m_elfData); - /* e_flags = */ getWord(data, m_elfData); - - quint32 e_shsize = getHalfWord(data, m_elfData); - - if (e_shsize > fdlen) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shsize")); - return Corrupt; - } - - quint32 e_phentsize = getHalfWord(data, m_elfData); - if (e_phentsize != (is64Bit ? 56 : 32)) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("invalid structure")); - return ElfReader::Corrupt; - } - quint32 e_phnum = getHalfWord(data, m_elfData); - - quint32 e_shentsize = getHalfWord(data, m_elfData); - - if (e_shentsize % 4) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shentsize")); - return Corrupt; - } - - quint32 e_shnum = getHalfWord(data, m_elfData); - quint32 e_shtrndx = getHalfWord(data, m_elfData); - if (data != mapper.ustart + (is64Bit ? 64 : 52)) { - m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_phentsize")); - return ElfReader::Corrupt; - } - - if (quint64(e_shnum) * e_shentsize > fdlen) { - const QString reason = QStringLiteral("announced %1 sections, each %2 bytes, exceed file size").arg(e_shnum).arg(e_shentsize); - m_errorString = msgInvalidElfObject(m_binary, reason); - return Corrupt; - } - - quint64 soff = e_shoff + e_shentsize * e_shtrndx; - -// if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) { -// m_errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)") -// .arg(m_binary) -// .arg(QLatin1String("shstrtab section header seems to be at %1")) -// .arg(QString::number(soff, 16)); -// return Corrupt; -// } - - if (e_shoff) { - ElfSectionHeader strtab; - parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData); - const quint64 stringTableFileOffset = strtab.offset; - if (quint32(stringTableFileOffset + e_shentsize) >= fdlen - || stringTableFileOffset == 0) { - const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16); - m_errorString = msgInvalidElfObject(m_binary, reason); - return Corrupt; - } - - for (quint32 i = 0; i < e_shnum; ++i) { - const uchar *s = mapper.ustart + e_shoff + i * e_shentsize; - ElfSectionHeader sh; - parseSectionHeader(s, &sh, m_elfData); - - if (stringTableFileOffset + sh.index > fdlen) { - const QString reason = QStringLiteral("section name %1 of %2 behind end of file") - .arg(i).arg(e_shnum); - m_errorString = msgInvalidElfObject(m_binary, reason); - return Corrupt; - } - - sh.name = mapper.start + stringTableFileOffset + sh.index; - if (sh.name == ".gdb_index") { - m_elfData.symbolsType = FastSymbols; - } else if (sh.name == ".debug_info") { - m_elfData.symbolsType = PlainSymbols; - } else if (sh.name == ".gnu_debuglink") { - m_elfData.debugLink = QByteArray(mapper.start + sh.offset); - m_elfData.symbolsType = LinkedSymbols; - } else if (sh.name == ".note.gnu.build-id") { - m_elfData.symbolsType = BuildIdSymbols; - if (sh.size > 16) - m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16, - int(sh.size) - 16).toHex(); - } - m_elfData.sectionHeaders.append(sh); - } - } - - if (e_phoff) { - for (quint32 i = 0; i < e_phnum; ++i) { - const uchar *s = mapper.ustart + e_phoff + i * e_phentsize; - ElfProgramHeader ph; - parseProgramHeader(s, &ph, m_elfData); - m_elfData.programHeaders.append(ph); - } - } - return Ok; -} - -QByteArray ElfReader::readSection(const QByteArray &name) -{ - readIt(); - int i = m_elfData.indexOf(name); - if (i == -1) - return QByteArray(); - - ElfMapper mapper(this); - if (!mapper.map()) - return QByteArray(); - - const ElfSectionHeader §ion = m_elfData.sectionHeaders.at(i); - return QByteArray(mapper.start + section.offset, int(section.size)); -} - -static QByteArray cutout(const char *s) -{ - QByteArray res(s, 80); - const int pos = res.indexOf('\0'); - if (pos != -1) - res.resize(pos - 1); - return res; -} - -QByteArray ElfReader::readCoreName(bool *isCore) -{ - *isCore = false; - - readIt(); - - ElfMapper mapper(this); - if (!mapper.map()) - return QByteArray(); - - if (m_elfData.elftype != Elf_ET_CORE) - return QByteArray(); - - *isCore = true; - - for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i) - if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) { - const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i); - return cutout(mapper.start + header.offset + 0x40); - } - - for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i) - if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) { - const ElfProgramHeader &header = m_elfData.programHeaders.at(i); - return cutout(mapper.start + header.offset + 0xec); - } - - return QByteArray(); -} - -int ElfData::indexOf(const QByteArray &name) const -{ - for (int i = 0, n = sectionHeaders.size(); i != n; ++i) - if (sectionHeaders.at(i).name == name) - return i; - return -1; -} - -/* Helpers for reading out the .dynamic section containing the dependencies. - * The ".dynamic" section is an array of - * typedef struct { - * Elf32_Sword d_tag; - * union { - * Elf32_Word d_val; - * dElf32_Addr d_ptr; - * } d_un; - * } Elf32_Dyn - * with entries where a tag DT_NEEDED indicates that m_val is an offset into - * the string table ".dynstr". The documentation states that entries with the - * tag DT_STRTAB contain an offset for the string table to be used, but that - * has been found not to contain valid entries. */ - -enum DynamicSectionTags { - DT_NULL = 0, - DT_NEEDED = 1, - DT_STRTAB = 5, - DT_SONAME = 14, - DT_RPATH = 15 -}; - -QList<QByteArray> ElfReader::dependencies() -{ - QList<QByteArray> result; - - ElfMapper mapper(this); - if (!mapper.map()) { - m_errorString = QStringLiteral("Mapper failure"); - return result; - } - quint64 dynStrOffset = 0; - quint64 dynamicOffset = 0; - quint64 dynamicSize = 0; - - const QList<ElfSectionHeader> &headers = readHeaders().sectionHeaders; - for (const ElfSectionHeader &eh : headers) { - if (eh.name == QByteArrayLiteral(".dynstr")) { - dynStrOffset = eh.offset; - } else if (eh.name == QByteArrayLiteral(".dynamic")) { - dynamicOffset = eh.offset; - dynamicSize = eh.size; - } - if (dynStrOffset && dynamicOffset) - break; - } - - if (!dynStrOffset || !dynamicOffset) { - m_errorString = QStringLiteral("Not a dynamically linked executable."); - return result; - } - - const unsigned char *dynamicData = mapper.ustart + dynamicOffset; - const unsigned char *dynamicDataEnd = dynamicData + dynamicSize; - while (dynamicData < dynamicDataEnd) { - const quint32 tag = getWord(dynamicData, m_elfData); - if (tag == DT_NULL) - break; - if (m_elfData.elfclass == Elf_ELFCLASS64) - dynamicData += sizeof(quint32); // padding to d_val/d_ptr. - if (tag == DT_NEEDED) { - const quint32 offset = getWord(dynamicData, m_elfData); - if (m_elfData.elfclass == Elf_ELFCLASS64) - dynamicData += sizeof(quint32); // past d_ptr. - const char *name = mapper.start + dynStrOffset + offset; - result.push_back(name); - } else { - dynamicData += m_elfData.elfclass == Elf_ELFCLASS64 ? 8 : 4; - } - } - return result; -} - -QT_END_NAMESPACE diff --git a/src/shared/winutils/elfreader.h b/src/shared/winutils/elfreader.h deleted file mode 100644 index d84f5a060..000000000 --- a/src/shared/winutils/elfreader.h +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef ELFREADER_H -#define ELFREADER_H - -#include <QtCore/QList> -#include <QtCore/QString> -#include <QtCore/QtEndian> - -QT_BEGIN_NAMESPACE - -enum ElfProgramHeaderType -{ - Elf_PT_NULL = 0, - Elf_PT_LOAD = 1, - Elf_PT_DYNAMIC = 2, - Elf_PT_INTERP = 3, - Elf_PT_NOTE = 4, - Elf_PT_SHLIB = 5, - Elf_PT_PHDR = 6, - Elf_PT_TLS = 7, - Elf_PT_NUM = 8 -}; - -enum ElfSectionHeaderType -{ - Elf_SHT_NULL = 0, - Elf_SHT_PROGBITS = 1, - Elf_SHT_SYMTAB = 2, - Elf_SHT_STRTAB = 3, - Elf_SHT_RELA = 4, - Elf_SHT_HASH = 5, - Elf_SHT_DYNAMIC = 6, - Elf_SHT_NOTE = 7, - Elf_SHT_NOBITS = 8, - Elf_SHT_REL = 9, - Elf_SHT_SHLIB = 10, - Elf_SHT_DYNSYM = 11, - Elf_SHT_INIT_ARRAY = 14, - Elf_SHT_FINI_ARRAY = 15, - Elf_SHT_PREINIT_ARRAY = 16, - Elf_SHT_GROUP = 17, - Elf_SHT_SYMTAB_SHNDX = 18 -}; - -enum ElfEndian -{ - Elf_ELFDATANONE = 0, - Elf_ELFDATA2LSB = 1, - Elf_ELFDATA2MSB = 2, - Elf_ELFDATANUM = 3 -}; - -enum ElfClass -{ - Elf_ELFCLASS32 = 1, - Elf_ELFCLASS64 = 2 -}; - -enum ElfType -{ - Elf_ET_NONE = 0, - Elf_ET_REL = 1, - Elf_ET_EXEC = 2, - Elf_ET_DYN = 3, - Elf_ET_CORE = 4 -}; - -enum ElfMachine -{ - Elf_EM_386 = 3, - Elf_EM_ARM = 40, - Elf_EM_X86_64 = 62 -}; - -enum DebugSymbolsType -{ - UnknownSymbols = 0, // Unknown. - NoSymbols = 1, // No usable symbols. - LinkedSymbols = 2, // Link to symols available. - BuildIdSymbols = 4, // BuildId available. - PlainSymbols = 8, // Ordinary symbols available. - FastSymbols = 16 // Dwarf index available. -}; - -class ElfSectionHeader -{ -public: - QByteArray name; - quint32 index; - quint32 type; - quint32 flags; - quint64 offset; - quint64 size; - quint64 addr; -}; - -class ElfProgramHeader -{ -public: - quint32 name; - quint32 type; - quint64 offset; - quint64 filesz; - quint64 memsz; -}; - -class ElfData -{ -public: - ElfData() : symbolsType(UnknownSymbols) {} - int indexOf(const QByteArray &name) const; - -public: - ElfEndian endian; - ElfType elftype; - ElfMachine elfmachine; - ElfClass elfclass; - quint64 entryPoint; - QByteArray debugLink; - QByteArray buildId; - DebugSymbolsType symbolsType; - QList<ElfSectionHeader> sectionHeaders; - QList<ElfProgramHeader> programHeaders; -}; - -class ElfReader -{ -public: - explicit ElfReader(const QString &binary); - enum Result { Ok, NotElf, Corrupt }; - - ElfData readHeaders(); - QByteArray readSection(const QByteArray §ionName); - QString errorString() const { return m_errorString; } - QByteArray readCoreName(bool *isCore); - QList<QByteArray> dependencies(); - -private: - friend class ElfMapper; - Result readIt(); - - QString m_binary; - QString m_errorString; - ElfData m_elfData; -}; - -QT_END_NAMESPACE - -#endif // ELFREADER_H diff --git a/src/shared/winutils/qmlutils.cpp b/src/shared/winutils/qmlutils.cpp deleted file mode 100644 index 6eebf6d86..000000000 --- a/src/shared/winutils/qmlutils.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qmlutils.h" -#include "utils.h" - -#include <QtCore/QDir> -#include <QtCore/QFileInfo> -#include <QtCore/QCoreApplication> -#include <QtCore/QJsonDocument> -#include <QtCore/QJsonObject> -#include <QtCore/QJsonArray> -#include <QtCore/QJsonParseError> - -QT_BEGIN_NAMESPACE - -bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2) -{ - return m1.className.isEmpty() ? m1.name == m2.name : m1.className == m2.className; -} - -// Return install path (cp -r semantics) -QString QmlImportScanResult::Module::installPath(const QString &root) const -{ - QString result = root; - const int lastSlashPos = relativePath.lastIndexOf(QLatin1Char('/')); - if (lastSlashPos != -1) { - result += QLatin1Char('/'); - result += QStringView{relativePath}.left(lastSlashPos); - } - return result; -} - -static QString qmlDirectoryRecursion(Platform platform, const QString &path) -{ - QDir dir(path); - if (!dir.entryList(QStringList(QStringLiteral("*.qml")), QDir::Files, QDir::NoSort).isEmpty()) - return dir.path(); - const QFileInfoList &subDirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort); - for (const QFileInfo &subDirFi : subDirs) { - if (!isBuildDirectory(platform, subDirFi.fileName())) { - const QString subPath = qmlDirectoryRecursion(platform, subDirFi.absoluteFilePath()); - if (!subPath.isEmpty()) - return subPath; - } - } - return QString(); -} - -// Find a directory containing QML files in the project -QString findQmlDirectory(Platform platform, const QString &startDirectoryName) -{ - QDir startDirectory(startDirectoryName); - if (isBuildDirectory(platform, startDirectory.dirName())) - startDirectory.cdUp(); - return qmlDirectoryRecursion(platform, startDirectory.path()); -} - -static void findFileRecursion(const QDir &directory, Platform platform, - DebugMatchMode debugMatchMode, QStringList *matches) -{ - const QStringList &dlls = findSharedLibraries(directory, platform, debugMatchMode); - for (const QString &dll : dlls) - matches->append(directory.filePath(dll)); - const QFileInfoList &subDirs = directory.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); - for (const QFileInfo &subDirFi : subDirs) { - QDir subDirectory(subDirFi.absoluteFilePath()); - if (subDirectory.isReadable()) - findFileRecursion(subDirectory, platform, debugMatchMode, matches); - } -} - -QmlImportScanResult runQmlImportScanner(const QString &directory, const QStringList &qmlImportPaths, - bool usesWidgets, int platform, DebugMatchMode debugMatchMode, - QString *errorMessage) -{ - Q_UNUSED(usesWidgets); - QmlImportScanResult result; - QStringList arguments; - for (const QString &importPath : qmlImportPaths) - arguments << QStringLiteral("-importPath") << importPath; - arguments << QStringLiteral("-rootPath") << directory; - unsigned long exitCode; - QByteArray stdOut; - QByteArray stdErr; - const QString binary = QStringLiteral("qmlimportscanner"); - if (!runProcess(binary, arguments, QDir::currentPath(), &exitCode, &stdOut, &stdErr, errorMessage)) - return result; - if (exitCode) { - *errorMessage = binary + QStringLiteral(" returned ") + QString::number(exitCode) - + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr); - return result; - } - QJsonParseError jsonParseError{}; - const QJsonDocument data = QJsonDocument::fromJson(stdOut, &jsonParseError); - if (data.isNull() ) { - *errorMessage = binary + QStringLiteral(" returned invalid JSON output: ") - + jsonParseError.errorString() + QStringLiteral(" :\"") - + QString::fromLocal8Bit(stdOut) + QLatin1Char('"'); - return result; - } - const QJsonArray array = data.array(); - const int childCount = array.count(); - for (int c = 0; c < childCount; ++c) { - const QJsonObject object = array.at(c).toObject(); - if (object.value(QStringLiteral("type")).toString() == QLatin1String("module")) { - const QString path = object.value(QStringLiteral("path")).toString(); - if (!path.isEmpty()) { - QmlImportScanResult::Module module; - module.name = object.value(QStringLiteral("name")).toString(); - module.className = object.value(QStringLiteral("classname")).toString(); - module.sourcePath = path; - module.relativePath = object.value(QStringLiteral("relativePath")).toString(); - result.modules.append(module); - findFileRecursion(QDir(path), Platform(platform), debugMatchMode, &result.plugins); - } - } - } - result.ok = true; - return result; -} - -void QmlImportScanResult::append(const QmlImportScanResult &other) -{ - for (const QmlImportScanResult::Module &module : other.modules) { - if (std::find(modules.cbegin(), modules.cend(), module) == modules.cend()) - modules.append(module); - } - for (const QString &plugin : other.plugins) { - if (!plugins.contains(plugin)) - plugins.append(plugin); - } -} - -QT_END_NAMESPACE diff --git a/src/shared/winutils/qmlutils.h b/src/shared/winutils/qmlutils.h deleted file mode 100644 index 572be7cab..000000000 --- a/src/shared/winutils/qmlutils.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QMLUTILS_H -#define QMLUTILS_H - -#include "utils.h" - -#include <QStringList> - -QT_BEGIN_NAMESPACE - -QString findQmlDirectory(Platform platform, const QString &startDirectoryName); - -struct QmlImportScanResult { - struct Module { - QString installPath(const QString &root) const; - - QString name; - QString className; - QString sourcePath; - QString relativePath; - }; - - void append(const QmlImportScanResult &other); - - bool ok = false; - QList<Module> modules; - QStringList plugins; -}; - -bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2); - -QmlImportScanResult runQmlImportScanner(const QString &directory, const QStringList &qmlImportPaths, - bool usesWidgets, int platform, DebugMatchMode debugMatchMode, - QString *errorMessage); - -QT_END_NAMESPACE - -#endif // QMLUTILS_H diff --git a/src/shared/winutils/utils.cpp b/src/shared/winutils/utils.cpp deleted file mode 100644 index bc52de924..000000000 --- a/src/shared/winutils/utils.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "utils.h" -#include "elfreader.h" - -#include <QtCore/QString> -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore/QTemporaryFile> -#include <QtCore/QScopedPointer> -#include <QtCore/QScopedArrayPointer> -#include <QtCore/QStandardPaths> -#if defined(Q_OS_WIN) -# include <QtCore/qt_windows.h> -# include <shlwapi.h> -# include <delayimp.h> -#else // Q_OS_WIN -# include <sys/wait.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <unistd.h> -# include <stdlib.h> -# include <string.h> -# include <errno.h> -# include <fcntl.h> -#endif // !Q_OS_WIN - -QT_BEGIN_NAMESPACE - -int optVerboseLevel = 1; - -bool isBuildDirectory(Platform platform, const QString &dirName) -{ - return (platform.testFlag(Msvc) || platform.testFlag(ClangMsvc)) - && (dirName == QLatin1String("debug") || dirName == QLatin1String("release")); -} - -// Create a symbolic link by changing to the source directory to make sure the -// link uses relative paths only (QFile::link() otherwise uses the absolute path). -bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage) -{ - const QString oldDirectory = QDir::currentPath(); - if (!QDir::setCurrent(source.absolutePath())) { - *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath())); - return false; - } - QFile file(source.fileName()); - const bool success = file.link(target); - QDir::setCurrent(oldDirectory); - if (!success) { - *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3") - .arg(QDir::toNativeSeparators(source.absoluteFilePath()), - QDir::toNativeSeparators(target), file.errorString()); - return false; - } - return true; -} - -bool createDirectory(const QString &directory, QString *errorMessage) -{ - const QFileInfo fi(directory); - if (fi.isDir()) - return true; - if (fi.exists()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a directory."). - arg(QDir::toNativeSeparators(directory)); - return false; - } - if (optVerboseLevel) - std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n"; - QDir dir; - if (!dir.mkpath(directory)) { - *errorMessage = QString::fromLatin1("Could not create directory %1."). - arg(QDir::toNativeSeparators(directory)); - return false; - } - return true; -} - -// Find shared libraries matching debug/Platform in a directory, return relative names. -QStringList findSharedLibraries(const QDir &directory, Platform platform, - DebugMatchMode debugMatchMode, - const QString &prefix) -{ - QString nameFilter = prefix; - if (nameFilter.isEmpty()) - nameFilter += QLatin1Char('*'); - if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)) - nameFilter += QLatin1Char('d'); - nameFilter += sharedLibrarySuffix(platform); - QStringList result; - QString errorMessage; - const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files); - for (const QFileInfo &dllFi : dlls) { - const QString dllPath = dllFi.absoluteFilePath(); - bool matches = true; - if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) { - bool debugDll; - if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll, - (platform == WindowsDesktopMinGW))) { - matches = debugDll == (debugMatchMode == MatchDebug); - } else { - std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath) - << ": " << errorMessage; - } - } // Windows - if (matches) - result += dllFi.fileName(); - } // for - return result; -} - -#ifdef Q_OS_WIN -QString winErrorMessage(unsigned long error) -{ - QString rc = QString::fromLatin1("#%1: ").arg(error); - char16_t *lpMsgBuf; - - const DWORD len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, int(len)); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1("<unknown error>"); - } - return rc; -} - -// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW() -QString normalizeFileName(const QString &name) -{ - wchar_t shortBuffer[MAX_PATH]; - const QString nativeFileName = QDir::toNativeSeparators(name); - if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH)) - return name; - wchar_t result[MAX_PATH]; - if (!GetLongPathNameW(shortBuffer, result, MAX_PATH)) - return name; - return QDir::fromNativeSeparators(QString::fromWCharArray(result)); -} - -// Find a tool binary in the Windows SDK 8 -QString findSdkTool(const QString &tool) -{ - QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';')); - const QByteArray sdkDir = qgetenv("WindowsSdkDir"); - if (!sdkDir.isEmpty()) - paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64")); - return QStandardPaths::findExecutable(tool, paths); -} - -// runProcess helper: Create a temporary file for stdout/stderr redirection. -static HANDLE createInheritableTemporaryFile() -{ - wchar_t path[MAX_PATH]; - if (!GetTempPath(MAX_PATH, path)) - return INVALID_HANDLE_VALUE; - wchar_t name[MAX_PATH]; - if (!GetTempFileName(path, L"temp", 0, name)) // Creates file. - return INVALID_HANDLE_VALUE; - SECURITY_ATTRIBUTES securityAttributes; - ZeroMemory(&securityAttributes, sizeof(securityAttributes)); - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.bInheritHandle = TRUE; - return CreateFile(name, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, - TRUNCATE_EXISTING, - FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); -} - -// runProcess helper: Rewind and read out a temporary file for stdout/stderr. -static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result) -{ - if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF) - return; - char buf[1024]; - DWORD bytesRead; - while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead) - result->append(buf, int(bytesRead)); - CloseHandle(handle); -} - -static inline void appendToCommandLine(const QString &argument, QString *commandLine) -{ - const bool needsQuote = argument.contains(QLatin1Char(' ')); - if (!commandLine->isEmpty()) - commandLine->append(QLatin1Char(' ')); - if (needsQuote) - commandLine->append(QLatin1Char('"')); - commandLine->append(argument); - if (needsQuote) - commandLine->append(QLatin1Char('"')); -} - -// runProcess: Run a command line process (replacement for QProcess which -// does not exist in the bootstrap library). -bool runProcess(const QString &binary, const QStringList &args, - const QString &workingDirectory, - unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr, - QString *errorMessage) -{ - if (exitCode) - *exitCode = 0; - - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - STARTUPINFO myInfo; - GetStartupInfo(&myInfo); - si.hStdInput = myInfo.hStdInput; - si.hStdOutput = myInfo.hStdOutput; - si.hStdError = myInfo.hStdError; - - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - const QChar backSlash = QLatin1Char('\\'); - QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory); - if (!nativeWorkingDir.endsWith(backSlash)) - nativeWorkingDir += backSlash; - - if (stdOut) { - si.hStdOutput = createInheritableTemporaryFile(); - if (si.hStdOutput == INVALID_HANDLE_VALUE) { - if (errorMessage) - *errorMessage = QStringLiteral("Error creating stdout temporary file"); - return false; - } - si.dwFlags |= STARTF_USESTDHANDLES; - } - - if (stdErr) { - si.hStdError = createInheritableTemporaryFile(); - if (si.hStdError == INVALID_HANDLE_VALUE) { - if (errorMessage) - *errorMessage = QStringLiteral("Error creating stderr temporary file"); - return false; - } - si.dwFlags |= STARTF_USESTDHANDLES; - } - - // Create a copy of the command line which CreateProcessW can modify. - QString commandLine; - appendToCommandLine(binary, &commandLine); - for (const QString &a : args) - appendToCommandLine(a, &commandLine); - if (optVerboseLevel > 1) - std::wcout << "Running: " << commandLine << '\n'; - - QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]); - commandLine.toWCharArray(commandLineW.data()); - commandLineW[commandLine.size()] = 0; - if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0, - reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) { - if (stdOut) - CloseHandle(si.hStdOutput); - if (stdErr) - CloseHandle(si.hStdError); - if (errorMessage) - *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError()); - return false; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - CloseHandle(pi.hThread); - if (exitCode) - GetExitCodeProcess(pi.hProcess, exitCode); - CloseHandle(pi.hProcess); - - if (stdOut) - readTemporaryProcessFile(si.hStdOutput, stdOut); - if (stdErr) - readTemporaryProcessFile(si.hStdError, stdErr); - return true; -} - -#else // Q_OS_WIN - -static inline char *encodeFileName(const QString &f) -{ - const QByteArray encoded = QFile::encodeName(f); - char *result = new char[encoded.size() + 1]; - strcpy(result, encoded.constData()); - return result; -} - -static inline char *tempFilePattern() -{ - QString path = QDir::tempPath(); - if (!path.endsWith(QLatin1Char('/'))) - path += QLatin1Char('/'); - path += QStringLiteral("tmpXXXXXX"); - return encodeFileName(path); -} - -static inline QByteArray readOutRedirectFile(int fd) -{ - enum { bufSize = 256 }; - - QByteArray result; - if (!lseek(fd, 0, 0)) { - char buf[bufSize]; - while (true) { - const ssize_t rs = read(fd, buf, bufSize); - if (rs <= 0) - break; - result.append(buf, int(rs)); - } - } - close(fd); - return result; -} - -// runProcess: Run a command line process (replacement for QProcess which -// does not exist in the bootstrap library). -bool runProcess(const QString &binary, const QStringList &args, - const QString &workingDirectory, - unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr, - QString *errorMessage) -{ - QScopedArrayPointer<char> stdOutFileName; - QScopedArrayPointer<char> stdErrFileName; - - int stdOutFile = 0; - if (stdOut) { - stdOutFileName.reset(tempFilePattern()); - stdOutFile = mkstemp(stdOutFileName.data()); - if (stdOutFile < 0) { - *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - } - - int stdErrFile = 0; - if (stdErr) { - stdErrFileName.reset(tempFilePattern()); - stdErrFile = mkstemp(stdErrFileName.data()); - if (stdErrFile < 0) { - *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - } - - const pid_t pID = fork(); - - if (pID < 0) { - *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - - if (!pID) { // Child - if (stdOut) { - dup2(stdOutFile, STDOUT_FILENO); - close(stdOutFile); - } - if (stdErr) { - dup2(stdErrFile, STDERR_FILENO); - close(stdErrFile); - } - - if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) { - std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n"; - ::_exit(-1); - } - - char **argv = new char *[args.size() + 2]; // Create argv. - char **ap = argv; - *ap++ = encodeFileName(binary); - for (const QString &a : qAsConst(args)) - *ap++ = encodeFileName(a); - *ap = 0; - - execvp(argv[0], argv); - ::_exit(-1); - } - - int status; - pid_t waitResult; - - do { - waitResult = waitpid(pID, &status, 0); - } while (waitResult == -1 && errno == EINTR); - - if (stdOut) { - *stdOut = readOutRedirectFile(stdOutFile); - unlink(stdOutFileName.data()); - } - if (stdErr) { - *stdErr = readOutRedirectFile(stdErrFile); - unlink(stdErrFileName.data()); - } - - if (waitResult < 0) { - *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - if (!WIFEXITED(status)) { - *errorMessage = binary + QStringLiteral(" did not exit cleanly."); - return false; - } - if (exitCode) - *exitCode = WEXITSTATUS(status); - return true; -} - -#endif // !Q_OS_WIN - -// Find a file in the path using ShellAPI. This can be used to locate DLLs which -// QStandardPaths cannot do. -QString findInPath(const QString &file) -{ -#if defined(Q_OS_WIN) - if (file.size() < MAX_PATH - 1) { - wchar_t buffer[MAX_PATH]; - file.toWCharArray(buffer); - buffer[file.size()] = 0; - if (PathFindOnPath(buffer, NULL)) - return QDir::cleanPath(QString::fromWCharArray(buffer)); - } - return QString(); -#else // Q_OS_WIN - return QStandardPaths::findExecutable(file); -#endif // !Q_OS_WIN -} - -const char *qmakeInfixKey = "QT_INFIX"; - -QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage) -{ - const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths"); - QByteArray stdOut; - QByteArray stdErr; - unsigned long exitCode = 0; - if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage)) - return QMap<QString, QString>(); - if (exitCode) { - *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode) - + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr); - return QMap<QString, QString>(); - } - const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(QLatin1Char('\r')); - QMap<QString, QString> result; - const int size = output.size(); - for (int pos = 0; pos < size; ) { - const int colonPos = output.indexOf(QLatin1Char(':'), pos); - if (colonPos < 0) - break; - int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1); - if (endPos < 0) - endPos = size; - const QString key = output.mid(pos, colonPos - pos); - const QString value = output.mid(colonPos + 1, endPos - colonPos - 1); - result.insert(key, value); - pos = endPos + 1; - } - QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri")); - if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - while (true) { - const QByteArray line = qconfigPriFile.readLine(); - if (line.isEmpty()) - break; - if (line.startsWith("QT_LIBINFIX")) { - const int pos = line.indexOf('='); - if (pos >= 0) { - const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed()); - if (!infix.isEmpty()) - result.insert(QLatin1String(qmakeInfixKey), infix); - } - break; - } - } - } else { - std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName()) - << ": " << qconfigPriFile.errorString()<< '\n'; - } - return result; -} - -// Update a file or directory. -bool updateFile(const QString &sourceFileName, const QStringList &nameFilters, - const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage) -{ - const QFileInfo sourceFileInfo(sourceFileName); - const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName(); - if (optVerboseLevel > 1) - std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n'; - - if (!sourceFileInfo.exists()) { - *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - if (sourceFileInfo.isSymLink()) { - *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).") - .arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - const QFileInfo targetFileInfo(targetFileName); - - if (sourceFileInfo.isDir()) { - if (targetFileInfo.exists()) { - if (!targetFileInfo.isDir()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.") - .arg(QDir::toNativeSeparators(targetFileName)); - return false; - } // Not a directory. - } else { // exists. - QDir d(targetDirectory); - if (optVerboseLevel) - std::wcout << "Creating " << QDir::toNativeSeparators(targetFileName) << ".\n"; - if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) { - *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.") - .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory)); - return false; - } - } - // Recurse into directory - QDir dir(sourceFileName); - const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files) - + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QFileInfo &entryFi : allEntries) { - if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage)) - return false; - } - return true; - } // Source is directory. - - if (targetFileInfo.exists()) { - if (!(flags & ForceUpdateFile) - && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) { - if (optVerboseLevel) - std::wcout << sourceFileInfo.fileName() << " is up to date.\n"; - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; - } - QFile targetFile(targetFileName); - if (!(flags & SkipUpdateFile) && !targetFile.remove()) { - *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2") - .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString()); - return false; - } - } // target exists - QFile file(sourceFileName); - if (optVerboseLevel) - std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n"; - if (!(flags & SkipUpdateFile) && !file.copy(targetFileName)) { - *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3") - .arg(QDir::toNativeSeparators(sourceFileName), - QDir::toNativeSeparators(targetFileName), - file.errorString()); - return false; - } - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; -} - -bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage, - QStringList *dependentLibraries, unsigned *wordSize, - bool *isDebug) -{ - ElfReader elfReader(elfExecutableFileName); - const ElfData data = elfReader.readHeaders(); - if (data.sectionHeaders.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - if (wordSize) - *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32; - if (dependentLibraries) { - dependentLibraries->clear(); - const QList<QByteArray> libs = elfReader.dependencies(); - if (libs.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - for (const QByteArray &l : libs) - dependentLibraries->push_back(QString::fromLocal8Bit(l)); - } - if (isDebug) - *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols; - return true; -} - -#ifdef Q_OS_WIN - -static inline QString stringFromRvaPtr(const void *rvaPtr) -{ - return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr)); -} - -// Helper for reading out PE executable files: Find a section header for an RVA -// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32). -template <class ImageNtHeader> -const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader) -{ - const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader); - const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections; - for ( ; section < sectionEnd; ++section) - if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize)) - return section; - return 0; -} - -// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32). -template <class ImageNtHeader> -inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase) -{ - const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader); - if (!sectionHdr) - return 0; - const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData; - return static_cast<const char *>(imageBase) + rva - delta; -} - -// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32 -template <class ImageNtHeader> -inline unsigned ntHeaderWordSize(const ImageNtHeader *header) -{ - // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC - enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b }; - if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic) - return 32; - if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic) - return 64; - return 0; -} - -// Helper for reading out PE executable files: Retrieve the NT image header of an -// executable via the legacy DOS header. -static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage) -{ - IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory); - // Check DOS header consistency - if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER)) - || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { - *errorMessage = QString::fromLatin1("DOS header check failed."); - return 0; - } - // Retrieve NT header - char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew; - IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC); - // check NT header consistency - if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature)) - || ntHeaders->Signature != IMAGE_NT_SIGNATURE - || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) { - *errorMessage = QString::fromLatin1("NT header check failed."); - return 0; - } - // Check magic - if (!ntHeaderWordSize(ntHeaders)) { - *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid."). - arg(ntHeaders->OptionalHeader.Magic); - return 0; - } - // Check section headers - IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders); - if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) { - *errorMessage = QString::fromLatin1("NT header section header check failed."); - return 0; - } - return ntHeaders; -} - -// Helper for reading out PE executable files: Read out import sections from -// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32. -template <class ImageNtHeader> -inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage) -{ - // Get import directory entry RVA and read out - const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - if (!importsStartRVA) { - *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry."); - return QStringList(); - } - const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base)); - if (!importDesc) { - *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry."); - return QStringList(); - } - QStringList result; - for ( ; importDesc->Name; ++importDesc) - result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base))); - - // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx . - // Check on grAttr bit 1 whether this is the format using RVA's > VS 6 - if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) { - const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base)); - for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc) - result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base))); - } - - return result; -} - -// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll, -// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll). -enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime }; - -static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries) -{ - for (const QString &lib : dependentLibraries) { - int pos = 0; - if (lib.startsWith(QLatin1String("MSVCR"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("MSVCP"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("VCRUNTIME"), Qt::CaseInsensitive)) { - int lastDotPos = lib.lastIndexOf(QLatin1Char('.')); - pos = -1 == lastDotPos ? 0 : lastDotPos - 1; - } - - if (pos > 0 && lib.contains(QLatin1String("_app"), Qt::CaseInsensitive)) - pos -= 4; - - if (pos) { - return lib.at(pos).toLower() == QLatin1Char('d') - ? MsvcDebugRuntime : MsvcReleaseRuntime; - } - } - return NoMsvcRuntime; -} - -template <class ImageNtHeader> -inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory, - bool isMinGW, - QStringList *dependentLibrariesIn, - bool *isDebugIn, QString *errorMessage) -{ - const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; - QStringList dependentLibraries; - if (dependentLibrariesIn || (isDebugIn != nullptr && hasDebugEntry && !isMinGW)) - dependentLibraries = readImportSections(nth, fileMemory, errorMessage); - - if (dependentLibrariesIn) - *dependentLibrariesIn = dependentLibraries; - if (isDebugIn != nullptr) { - if (isMinGW) { - // Use logic that's used e.g. in objdump / pfd library - *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED); - } else { - // When an MSVC debug entry is present, check whether the debug runtime - // is actually used to detect -release / -force-debug-info builds. - *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime; - } - } -} - -// Read a PE executable and determine dependent libraries, word size -// and debug flags. -bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage, - QStringList *dependentLibrariesIn, unsigned *wordSizeIn, - bool *isDebugIn, bool isMinGW, unsigned short *machineArchIn) -{ - bool result = false; - HANDLE hFile = NULL; - HANDLE hFileMap = NULL; - void *fileMemory = 0; - - if (dependentLibrariesIn) - dependentLibrariesIn->clear(); - if (wordSizeIn) - *wordSizeIn = 0; - if (isDebugIn) - *isDebugIn = false; - - do { - // Create a memory mapping of the file - hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) { - *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (hFileMap == NULL) { - *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); - if (!fileMemory) { - *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage); - if (!ntHeaders) - break; - - const unsigned wordSize = ntHeaderWordSize(ntHeaders); - if (wordSizeIn) - *wordSizeIn = wordSize; - if (wordSize == 32) { - determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); - } else { - determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); - } - - if (machineArchIn) - *machineArchIn = ntHeaders->FileHeader.Machine; - - result = true; - if (optVerboseLevel > 1) { - std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName) - << ' ' << wordSize << " bit"; - if (isMinGW) - std::wcout << ", MinGW"; - if (dependentLibrariesIn) { - std::wcout << ", dependent libraries: "; - if (optVerboseLevel > 2) - std::wcout << dependentLibrariesIn->join(QLatin1Char(' ')); - else - std::wcout << dependentLibrariesIn->size(); - } - if (isDebugIn) - std::wcout << (*isDebugIn ? ", debug" : ", release"); - std::wcout << '\n'; - } - } while (false); - - if (fileMemory) - UnmapViewOfFile(fileMemory); - - if (hFileMap != NULL) - CloseHandle(hFileMap); - - if (hFile != NULL && hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - - return result; -} - -QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize) -{ - const QString prefix = QStringLiteral("D3Dcompiler_"); - const QString suffix = QLatin1String(windowsSharedLibrarySuffix); - // Get the DLL from Kit 8.0 onwards - const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir")); - if (!kitDir.isEmpty()) { - QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/"); - if (platform.testFlag(ArmBased)) { - redistDirPath += QStringLiteral("arm"); - } else { - redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64"); - } - QDir redistDir(redistDirPath); - if (redistDir.exists()) { - const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files); - if (!files.isEmpty()) - return files.front().absoluteFilePath(); - } - } - QStringList candidateVersions; - for (int i = 47 ; i >= 40 ; --i) - candidateVersions.append(prefix + QString::number(i) + suffix); - // Check the bin directory of the Qt SDK (in case it is shadowed by the - // Windows system directory in PATH). - for (const QString &candidate : qAsConst(candidateVersions)) { - const QFileInfo fi(qtBinDir + QLatin1Char('/') + candidate); - if (fi.isFile()) - return fi.absoluteFilePath(); - } - // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47). - if (platform.testFlag(IntelBased)) { - QString errorMessage; - unsigned detectedWordSize; - for (const QString &candidate : qAsConst(candidateVersions)) { - const QString dll = findInPath(candidate); - if (!dll.isEmpty() - && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0) - && detectedWordSize == wordSize) { - return dll; - } - } - } - return QString(); -} - -#else // Q_OS_WIN - -bool readPeExecutable(const QString &, QString *errorMessage, - QStringList *, unsigned *, bool *, bool, unsigned short *) -{ - *errorMessage = QStringLiteral("Not implemented."); - return false; -} - -QString findD3dCompiler(Platform, const QString &, unsigned) -{ - return QString(); -} - -#endif // !Q_OS_WIN - -// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=." -bool patchQtCore(const QString &path, QString *errorMessage) -{ - if (optVerboseLevel) - std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n"; - - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg( - QDir::toNativeSeparators(path), file.errorString()); - return false; - } - const QByteArray oldContent = file.readAll(); - - if (oldContent.isEmpty()) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg( - QDir::toNativeSeparators(path)); - return false; - } - file.close(); - - QByteArray content = oldContent; - - QByteArray prfxpath("qt_prfxpath="); - int startPos = content.indexOf(prfxpath); - if (startPos == -1) { - *errorMessage = QString::fromLatin1( - "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg( - QDir::toNativeSeparators(path)); - return false; - } - startPos += prfxpath.length(); - int endPos = content.indexOf(char(0), startPos); - if (endPos == -1) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg( - QDir::toNativeSeparators(path)); - return false; - } - - QByteArray replacement = QByteArray(endPos - startPos, char(0)); - replacement[0] = '.'; - content.replace(startPos, endPos - startPos, replacement); - if (content == oldContent) - return true; - - if (!file.open(QIODevice::WriteOnly) - || (file.write(content) != content.size())) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file: %2").arg( - QDir::toNativeSeparators(path), file.errorString()); - return false; - } - return true; -} - -#ifdef Q_OS_WIN -QString getArchString(unsigned short machineArch) -{ - switch (machineArch) { - case IMAGE_FILE_MACHINE_I386: - return QStringLiteral("x86"); - case IMAGE_FILE_MACHINE_ARM: - return QStringLiteral("arm"); - case IMAGE_FILE_MACHINE_AMD64: - return QStringLiteral("x64"); - case IMAGE_FILE_MACHINE_ARM64: - return QStringLiteral("arm64"); - default: - break; - } - return QString(); -} -#endif // Q_OS_WIN - -QT_END_NAMESPACE diff --git a/src/shared/winutils/utils.h b/src/shared/winutils/utils.h deleted file mode 100644 index 0fe3a6948..000000000 --- a/src/shared/winutils/utils.h +++ /dev/null @@ -1,404 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef UTILS_H -#define UTILS_H - -#include <QStringList> -#include <QMap> -#include <QtCore/QFile> -#include <QtCore/QDir> -#include <QtCore/QDateTime> -#include <QtCore/QJsonArray> -#include <QtCore/QJsonObject> -#include <QtCore/QJsonDocument> - -#include <iostream> - -QT_BEGIN_NAMESPACE - -enum PlatformFlag { - // OS - WindowsBased = 0x00001, - UnixBased = 0x00002, - // CPU - IntelBased = 0x00010, - ArmBased = 0x00020, - // Compiler - Msvc = 0x00100, - MinGW = 0x00200, - ClangMsvc = 0x00400, - ClangMinGW = 0x00800, - // Platforms - WindowsDesktopMsvc = WindowsBased + IntelBased + Msvc, - WindowsDesktopMinGW = WindowsBased + IntelBased + MinGW, - WindowsDesktopClangMsvc = WindowsBased + IntelBased + ClangMsvc, - WindowsDesktopClangMinGW = WindowsBased + IntelBased + ClangMinGW, - Unix = UnixBased, - UnknownPlatform -}; - -Q_DECLARE_FLAGS(Platform, PlatformFlag) - -Q_DECLARE_OPERATORS_FOR_FLAGS(Platform) - -inline bool platformHasDebugSuffix(Platform p) // Uses 'd' debug suffix -{ - return p.testFlag(Msvc) || p.testFlag(ClangMsvc); -} - -enum ListOption { - ListNone = 0, - ListSource, - ListTarget, - ListRelative, - ListMapping -}; - -inline std::wostream &operator<<(std::wostream &str, const QString &s) -{ -#ifdef Q_OS_WIN - str << reinterpret_cast<const wchar_t *>(s.utf16()); -#else - str << s.toStdWString(); -#endif - return str; -} - -// Container class for JSON output -class JsonOutput -{ - using SourceTargetMapping = QPair<QString, QString>; - using SourceTargetMappings = QList<SourceTargetMapping>; - -public: - void addFile(const QString &source, const QString &target) - { - m_files.append(SourceTargetMapping(source, target)); - } - - void removeTargetDirectory(const QString &targetDirectory) - { - for (int i = m_files.size() - 1; i >= 0; --i) { - if (m_files.at(i).second == targetDirectory) - m_files.removeAt(i); - } - } - - QByteArray toJson() const - { - QJsonObject document; - QJsonArray files; - for (const SourceTargetMapping &mapping : m_files) { - QJsonObject object; - object.insert(QStringLiteral("source"), QDir::toNativeSeparators(mapping.first)); - object.insert(QStringLiteral("target"), QDir::toNativeSeparators(mapping.second)); - files.append(object); - } - document.insert(QStringLiteral("files"), files); - return QJsonDocument(document).toJson(); - } - QByteArray toList(ListOption option, const QDir &base) const - { - QByteArray list; - for (const SourceTargetMapping &mapping : m_files) { - const QString source = QDir::toNativeSeparators(mapping.first); - const QString fileName = QFileInfo(mapping.first).fileName(); - const QString target = QDir::toNativeSeparators(mapping.second) + QDir::separator() + fileName; - switch (option) { - case ListNone: - break; - case ListSource: - list += source.toUtf8() + '\n'; - break; - case ListTarget: - list += target.toUtf8() + '\n'; - break; - case ListRelative: - list += QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + '\n'; - break; - case ListMapping: - list += '"' + source.toUtf8() + "\" \"" + QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + "\"\n"; - break; - } - } - return list; - } -private: - SourceTargetMappings m_files; -}; - -#ifdef Q_OS_WIN -QString normalizeFileName(const QString &name); -QString winErrorMessage(unsigned long error); -QString findSdkTool(const QString &tool); -#else // !Q_OS_WIN -inline QString normalizeFileName(const QString &name) { return name; } -#endif // !Q_OS_WIN - -static const char windowsSharedLibrarySuffix[] = ".dll"; -static const char unixSharedLibrarySuffix[] = ".so"; - -inline QString sharedLibrarySuffix(Platform platform) { return QLatin1String((platform & WindowsBased) ? windowsSharedLibrarySuffix : unixSharedLibrarySuffix); } -bool isBuildDirectory(Platform platform, const QString &dirName); - -bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage); -bool createDirectory(const QString &directory, QString *errorMessage); -QString findInPath(const QString &file); - -extern const char *qmakeInfixKey; // Fake key containing the libinfix - -QMap<QString, QString> queryQtPaths(const QString &qmakeBinary, QString *errorMessage); - -enum DebugMatchMode { - MatchDebug, - MatchRelease, - MatchDebugOrRelease -}; - -QStringList findSharedLibraries(const QDir &directory, Platform platform, - DebugMatchMode debugMatchMode, - const QString &prefix = QString()); - -bool updateFile(const QString &sourceFileName, const QStringList &nameFilters, - const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage); -bool runProcess(const QString &binary, const QStringList &args, - const QString &workingDirectory = QString(), - unsigned long *exitCode = 0, QByteArray *stdOut = 0, QByteArray *stdErr = 0, - QString *errorMessage = 0); - -bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage, - QStringList *dependentLibraries = 0, unsigned *wordSize = 0, - bool *isDebug = 0, bool isMinGW = false, unsigned short *machineArch = nullptr); -bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage, - QStringList *dependentLibraries = 0, unsigned *wordSize = 0, - bool *isDebug = 0); - -inline bool readExecutable(const QString &executableFileName, Platform platform, - QString *errorMessage, QStringList *dependentLibraries = 0, - unsigned *wordSize = 0, bool *isDebug = 0, unsigned short *machineArch = nullptr) -{ - return platform == Unix ? - readElfExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug) : - readPeExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug, - (platform == WindowsDesktopMinGW), machineArch); -} - -#ifdef Q_OS_WIN -# if !defined(IMAGE_FILE_MACHINE_ARM64) -# define IMAGE_FILE_MACHINE_ARM64 0xAA64 -# endif -QString getArchString (unsigned short machineArch); -#endif // Q_OS_WIN - -// Return dependent modules of executable files. - -inline QStringList findDependentLibraries(const QString &executableFileName, Platform platform, QString *errorMessage) -{ - QStringList result; - readExecutable(executableFileName, platform, errorMessage, &result); - return result; -} - -QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize); - -bool patchQtCore(const QString &path, QString *errorMessage); - -extern int optVerboseLevel; - -// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir -// to obtain the files. -enum UpdateFileFlag { - ForceUpdateFile = 0x1, - SkipUpdateFile = 0x2, - RemoveEmptyQmlDirectories = 0x4, - SkipQmlDesignerSpecificsDirectories = 0x8 -}; - -template <class DirectoryFileEntryFunction> -bool updateFile(const QString &sourceFileName, - DirectoryFileEntryFunction directoryFileEntryFunction, - const QString &targetDirectory, - unsigned flags, - JsonOutput *json, - QString *errorMessage) -{ - const QFileInfo sourceFileInfo(sourceFileName); - const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName(); - if (optVerboseLevel > 1) - std::wcout << "Checking " << sourceFileName << ", " << targetFileName << '\n'; - - if (!sourceFileInfo.exists()) { - *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - const QFileInfo targetFileInfo(targetFileName); - - if (sourceFileInfo.isSymLink()) { - const QString sourcePath = sourceFileInfo.symLinkTarget(); - const QString relativeSource = QDir(sourceFileInfo.absolutePath()).relativeFilePath(sourcePath); - if (relativeSource.contains(QLatin1Char('/'))) { - *errorMessage = QString::fromLatin1("Symbolic links across directories are not supported (%1).") - .arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - // Update the linked-to file - if (!updateFile(sourcePath, directoryFileEntryFunction, targetDirectory, flags, json, errorMessage)) - return false; - - if (targetFileInfo.exists()) { - if (!targetFileInfo.isSymLink()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a symbolic link.") - .arg(QDir::toNativeSeparators(targetFileName)); - return false; - } // Not a symlink - const QString relativeTarget = QDir(targetFileInfo.absolutePath()).relativeFilePath(targetFileInfo.symLinkTarget()); - if (relativeSource == relativeTarget) // Exists and points to same entry: happy. - return true; - QFile existingTargetFile(targetFileName); - if (!(flags & SkipUpdateFile) && !existingTargetFile.remove()) { - *errorMessage = QString::fromLatin1("Cannot remove existing symbolic link %1: %2") - .arg(QDir::toNativeSeparators(targetFileName), existingTargetFile.errorString()); - return false; - } - } // target symbolic link exists - return createSymbolicLink(QFileInfo(targetDirectory + QLatin1Char('/') + relativeSource), sourceFileInfo.fileName(), errorMessage); - } // Source is symbolic link - - if (sourceFileInfo.isDir()) { - if ((flags & SkipQmlDesignerSpecificsDirectories) && sourceFileInfo.fileName() == QLatin1String("designer")) { - if (optVerboseLevel) - std::wcout << "Skipping " << QDir::toNativeSeparators(sourceFileName) << ".\n"; - return true; - } - bool created = false; - if (targetFileInfo.exists()) { - if (!targetFileInfo.isDir()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.") - .arg(QDir::toNativeSeparators(targetFileName)); - return false; - } // Not a directory. - } else { // exists. - QDir d(targetDirectory); - if (optVerboseLevel) - std::wcout << "Creating " << targetFileName << ".\n"; - if (!(flags & SkipUpdateFile)) { - created = d.mkdir(sourceFileInfo.fileName()); - if (!created) { - *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.") - .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory)); - return false; - } - } - } - // Recurse into directory - QDir dir(sourceFileName); - - const QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &entry : allEntries) - if (!updateFile(sourceFileName + QLatin1Char('/') + entry, directoryFileEntryFunction, targetFileName, flags, json, errorMessage)) - return false; - // Remove empty directories, for example QML import folders for which the filter did not match. - if (created && (flags & RemoveEmptyQmlDirectories)) { - QDir d(targetFileName); - const QStringList entries = d.entryList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); - if (entries.isEmpty() || (entries.size() == 1 && entries.first() == QLatin1String("qmldir"))) { - if (!d.removeRecursively()) { - *errorMessage = QString::fromLatin1("Cannot remove empty directory %1.") - .arg(QDir::toNativeSeparators(targetFileName)); - return false; - } - if (json) - json->removeTargetDirectory(targetFileName); - } - } - return true; - } // Source is directory. - - if (targetFileInfo.exists()) { - if (!(flags & ForceUpdateFile) - && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) { - if (optVerboseLevel) - std::wcout << sourceFileInfo.fileName() << " is up to date.\n"; - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; - } - QFile targetFile(targetFileName); - if (!(flags & SkipUpdateFile) && !targetFile.remove()) { - *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2") - .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString()); - return false; - } - } // target exists - QFile file(sourceFileName); - if (optVerboseLevel) - std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n"; - if (!(flags & SkipUpdateFile)) { - if (!file.copy(targetFileName)) { - *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3") - .arg(QDir::toNativeSeparators(sourceFileName), - QDir::toNativeSeparators(targetFileName), - file.errorString()); - return false; - } - if (!(file.permissions() & QFile::WriteUser)) { // QTBUG-40152, clear inherited read-only attribute - QFile targetFile(targetFileName); - if (!targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser)) { - *errorMessage = QString::fromLatin1("Cannot set write permission on %1: %2") - .arg(QDir::toNativeSeparators(targetFileName), file.errorString()); - return false; - } - } // Check permissions - } // !SkipUpdateFile - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; -} - -// Base class to filter files by name filters functions to be passed to updateFile(). -class NameFilterFileEntryFunction { -public: - explicit NameFilterFileEntryFunction(const QStringList &nameFilters) : m_nameFilters(nameFilters) {} - QStringList operator()(const QDir &dir) const { return dir.entryList(m_nameFilters, QDir::Files); } - -private: - const QStringList m_nameFilters; -}; - -// Convenience for all files. -inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage) -{ - return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, flags, json, errorMessage); -} - -QT_END_NAMESPACE - -#endif // UTILS_H diff --git a/src/windeployqt/CMakeLists.txt b/src/windeployqt/CMakeLists.txt deleted file mode 100644 index bc90d20ef..000000000 --- a/src/windeployqt/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# Generated from windeployqt.pro. - -##################################################################### -## windeployqt Tool: -##################################################################### - -qt_get_tool_target_name(target_name windeployqt) -qt_internal_add_tool(${target_name} - # BOOTSTRAP # special case remove - TOOLS_TARGET Tools # special case - TARGET_DESCRIPTION "Qt Windows Deployment Tool" - SOURCES - ../shared/winutils/elfreader.cpp ../shared/winutils/elfreader.h - ../shared/winutils/qmlutils.cpp ../shared/winutils/qmlutils.h - ../shared/winutils/utils.cpp ../shared/winutils/utils.h - main.cpp - DEFINES - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_FOREACH - INCLUDE_DIRECTORIES - ../shared/winutils - PUBLIC_LIBRARIES - Qt::CorePrivate - Qt::Core # special case -) -qt_internal_return_unless_building_tools() - -#### Keys ignored in scope 1:.:.:windeployqt.pro:<TRUE>: -# QMAKE_TARGET_DESCRIPTION = "Qt Windows Deployment Tool" -# QT_FOR_CONFIG = "tools-private" -# _OPTION = "host_build" -# _REQUIREMENTS = "qtConfig(windeployqt)" - -## Scopes: -##################################################################### - -qt_internal_extend_target(${target_name} CONDITION WIN32 - PUBLIC_LIBRARIES - shlwapi -) - -qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_relocatable - DEFINES - QT_RELOCATABLE -) diff --git a/src/windeployqt/main.cpp b/src/windeployqt/main.cpp deleted file mode 100644 index 27b57ea9a..000000000 --- a/src/windeployqt/main.cpp +++ /dev/null @@ -1,1723 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "utils.h" -#include "qmlutils.h" - -#include <QtCore/QCommandLineOption> -#include <QtCore/QCommandLineParser> -#include <QtCore/QDir> -#include <QtCore/QFileInfo> -#include <QtCore/QCoreApplication> -#include <QtCore/QJsonDocument> -#include <QtCore/QJsonObject> -#include <QtCore/QJsonArray> -#include <QtCore/QList> -#include <QtCore/QOperatingSystemVersion> -#include <QtCore/QSharedPointer> - -#ifdef Q_OS_WIN -#include <QtCore/qt_windows.h> -#else -#define IMAGE_FILE_MACHINE_ARM64 0xaa64 -#endif - -#include <algorithm> -#include <iostream> -#include <iterator> -#include <cstdio> - -QT_BEGIN_NAMESPACE - -enum QtModule -#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC) - : quint64 -#endif -{ - QtBluetoothModule = 0x0000000000000001, - QtConcurrentModule = 0x0000000000000002, - QtCoreModule = 0x0000000000000004, - QtDeclarativeModule = 0x0000000000000008, - QtDesignerComponents = 0x0000000000000010, - QtDesignerModule = 0x0000000000000020, - QtGuiModule = 0x0000000000000040, - QtHelpModule = 0x0000000000000080, - QtMultimediaModule = 0x0000000000000100, - QtMultimediaWidgetsModule = 0x0000000000000200, - QtMultimediaQuickModule = 0x0000000000000400, - QtNetworkModule = 0x0000000000000800, - QtNfcModule = 0x0000000000001000, - QtOpenGLModule = 0x0000000000002000, - QtOpenGLWidgetsModule = 0x0000000000004000, - QtPositioningModule = 0x0000000000008000, - QtPrintSupportModule = 0x0000000000010000, - QtQmlModule = 0x0000000000020000, - QtQuickModule = 0x0000000000040000, - QtQuickParticlesModule = 0x0000000000080000, - QtScriptModule = 0x0000000000100000, - QtScriptToolsModule = 0x0000000000200000, - QtSensorsModule = 0x0000000000400000, - QtSerialPortModule = 0x0000000000800000, - QtSqlModule = 0x0000000001000000, - QtSvgModule = 0x0000000002000000, - QtSvgWidgetsModule = 0x0000000004000000, - QtTestModule = 0x0000000008000000, - QtWidgetsModule = 0x0000000010000000, - QtWinExtrasModule = 0x0000000020000000, - QtXmlModule = 0x0000000040000000, - QtQuickWidgetsModule = 0x0000000100000000, - QtWebSocketsModule = 0x0000000200000000, - QtWebEngineCoreModule = 0x0000000800000000, - QtWebEngineModule = 0x0000001000000000, - QtWebEngineWidgetsModule = 0x0000002000000000, - QtQmlToolingModule = 0x0000004000000000, - Qt3DCoreModule = 0x0000008000000000, - Qt3DRendererModule = 0x0000010000000000, - Qt3DQuickModule = 0x0000020000000000, - Qt3DQuickRendererModule = 0x0000040000000000, - Qt3DInputModule = 0x0000080000000000, - QtLocationModule = 0x0000100000000000, - QtWebChannelModule = 0x0000200000000000, - QtTextToSpeechModule = 0x0000400000000000, - QtSerialBusModule = 0x0000800000000000, - QtGamePadModule = 0x0001000000000000, - Qt3DAnimationModule = 0x0002000000000000, - QtWebViewModule = 0x0004000000000000, - Qt3DExtrasModule = 0x0008000000000000, - QtShaderToolsModule = 0x0010000000000000 -}; - -struct QtModuleEntry { - quint64 module; - const char *option; - const char *libraryName; - const char *translation; -}; - -static QtModuleEntry qtModuleEntries[] = { - { QtBluetoothModule, "bluetooth", "Qt6Bluetooth", nullptr }, - { QtConcurrentModule, "concurrent", "Qt6Concurrent", "qtbase" }, - { QtCoreModule, "core", "Qt6Core", "qtbase" }, - { QtDeclarativeModule, "declarative", "Qt6Declarative", "qtquick1" }, - { QtDesignerModule, "designer", "Qt6Designer", nullptr }, - { QtDesignerComponents, "designercomponents", "Qt6DesignerComponents", nullptr }, - { QtGamePadModule, "gamepad", "Qt6Gamepad", nullptr }, - { QtGuiModule, "gui", "Qt6Gui", "qtbase" }, - { QtHelpModule, "qthelp", "Qt6Help", "qt_help" }, - { QtMultimediaModule, "multimedia", "Qt6Multimedia", "qtmultimedia" }, - { QtMultimediaWidgetsModule, "multimediawidgets", "Qt6MultimediaWidgets", "qtmultimedia" }, - { QtMultimediaQuickModule, "multimediaquick", "Qt6MultimediaQuick_p", "qtmultimedia" }, - { QtNetworkModule, "network", "Qt6Network", "qtbase" }, - { QtNfcModule, "nfc", "Qt6Nfc", nullptr }, - { QtOpenGLModule, "opengl", "Qt6OpenGL", nullptr }, - { QtOpenGLWidgetsModule, "openglwidgets", "Qt6OpenGLWidgets", nullptr }, - { QtPositioningModule, "positioning", "Qt6Positioning", nullptr }, - { QtPrintSupportModule, "printsupport", "Qt6PrintSupport", nullptr }, - { QtQmlModule, "qml", "Qt6Qml", "qtdeclarative" }, - { QtQmlToolingModule, "qmltooling", "qmltooling", nullptr }, - { QtQuickModule, "quick", "Qt6Quick", "qtdeclarative" }, - { QtQuickParticlesModule, "quickparticles", "Qt6QuickParticles", nullptr }, - { QtQuickWidgetsModule, "quickwidgets", "Qt6QuickWidgets", nullptr }, - { QtScriptModule, "script", "Qt6Script", "qtscript" }, - { QtScriptToolsModule, "scripttools", "Qt6ScriptTools", "qtscript" }, - { QtSensorsModule, "sensors", "Qt6Sensors", nullptr }, - { QtSerialPortModule, "serialport", "Qt6SerialPort", "qtserialport" }, - { QtSqlModule, "sql", "Qt6Sql", "qtbase" }, - { QtSvgModule, "svg", "Qt6Svg", nullptr }, - { QtSvgWidgetsModule, "svgwidgets", "Qt6SvgWidgets", nullptr }, - { QtTestModule, "test", "Qt6Test", "qtbase" }, - { QtWebSocketsModule, "websockets", "Qt6WebSockets", nullptr }, - { QtWidgetsModule, "widgets", "Qt6Widgets", "qtbase" }, - { QtWinExtrasModule, "winextras", "Qt6WinExtras", nullptr }, - { QtXmlModule, "xml", "Qt6Xml", "qtbase" }, - { QtWebEngineCoreModule, "webenginecore", "Qt6WebEngineCore", nullptr }, - { QtWebEngineModule, "webengine", "Qt6WebEngine", "qtwebengine" }, - { QtWebEngineWidgetsModule, "webenginewidgets", "Qt6WebEngineWidgets", nullptr }, - { Qt3DCoreModule, "3dcore", "Qt63DCore", nullptr }, - { Qt3DRendererModule, "3drenderer", "Qt63DRender", nullptr }, - { Qt3DQuickModule, "3dquick", "Qt63DQuick", nullptr }, - { Qt3DQuickRendererModule, "3dquickrenderer", "Qt63DQuickRender", nullptr }, - { Qt3DInputModule, "3dinput", "Qt63DInput", nullptr }, - { Qt3DAnimationModule, "3danimation", "Qt63DAnimation", nullptr }, - { Qt3DExtrasModule, "3dextras", "Qt63DExtras", nullptr }, - { QtLocationModule, "geoservices", "Qt6Location", nullptr }, - { QtWebChannelModule, "webchannel", "Qt6WebChannel", nullptr }, - { QtTextToSpeechModule, "texttospeech", "Qt6TextToSpeech", nullptr }, - { QtSerialBusModule, "serialbus", "Qt6SerialBus", nullptr }, - { QtWebViewModule, "webview", "Qt6WebView", nullptr }, - { QtShaderToolsModule, "shadertools", "Qt6ShaderTools", nullptr } -}; - -enum QtPlugin { - QtVirtualKeyboardPlugin = 0x1 -}; - -static const char webEngineProcessC[] = "QtWebEngineProcess"; - -static inline QString webProcessBinary(const char *binaryName, Platform p) -{ - const QString webProcess = QLatin1String(binaryName); - return (p & WindowsBased) ? webProcess + QStringLiteral(".exe") : webProcess; -} - -static QByteArray formatQtModules(quint64 mask, bool option = false) -{ - QByteArray result; - for (const auto &qtModule : qtModuleEntries) { - if (mask & qtModule.module) { - if (!result.isEmpty()) - result.append(' '); - result.append(option ? qtModule.option : qtModule.libraryName); - } - } - return result; -} - -static Platform platformFromMkSpec(const QString &xSpec) -{ - if (xSpec == QLatin1String("linux-g++")) - return Unix; - if (xSpec.startsWith(QLatin1String("win32-"))) { - if (xSpec.contains(QLatin1String("clang-g++"))) - return WindowsDesktopClangMinGW; - if (xSpec.contains(QLatin1String("clang-msvc++"))) - return WindowsDesktopClangMsvc; - return xSpec.contains(QLatin1String("g++")) ? WindowsDesktopMinGW : WindowsDesktopMsvc; - } - return UnknownPlatform; -} - -// Helpers for exclusive options, "-foo", "--no-foo" -enum ExlusiveOptionValue { - OptionAuto, - OptionEnabled, - OptionDisabled -}; - -static ExlusiveOptionValue parseExclusiveOptions(const QCommandLineParser *parser, - const QCommandLineOption &enableOption, - const QCommandLineOption &disableOption) -{ - const bool enabled = parser->isSet(enableOption); - const bool disabled = parser->isSet(disableOption); - if (enabled) { - if (disabled) { - std::wcerr << "Warning: both -" << enableOption.names().first() - << " and -" << disableOption.names().first() << " were specified, defaulting to -" - << enableOption.names().first() << ".\n"; - } - return OptionEnabled; - } - return disabled ? OptionDisabled : OptionAuto; -} - -struct Options { - enum DebugDetection { - DebugDetectionAuto, - DebugDetectionForceDebug, - DebugDetectionForceRelease - }; - - bool plugins = true; - bool libraries = true; - bool quickImports = true; - bool translations = true; - bool systemD3dCompiler = true; - bool compilerRunTime = false; - unsigned disabledPlugins = 0; - bool softwareRasterizer = true; - Platform platform = WindowsDesktopMsvc; - quint64 additionalLibraries = 0; - quint64 disabledLibraries = 0; - unsigned updateFileFlags = 0; - QStringList qmlDirectories; // Project's QML files. - QStringList qmlImportPaths; // Custom QML module locations. - QString directory; - QString qtpathsBinary; - QString translationsDirectory; // Translations target directory - QStringList languages; - QString libraryDirectory; - QString pluginDirectory; - QStringList binaries; - JsonOutput *json = nullptr; - ListOption list = ListNone; - DebugDetection debugDetection = DebugDetectionAuto; - bool deployPdb = false; - bool dryRun = false; - bool patchQt = true; - bool ignoreLibraryErrors = false; -}; - -// Return binary to be deployed from folder, ignore pre-existing web engine process. -static inline QString findBinary(const QString &directory, Platform platform) -{ - const QStringList nameFilters = (platform & WindowsBased) ? - QStringList(QStringLiteral("*.exe")) : QStringList(); - const QFileInfoList &binaries = - QDir(QDir::cleanPath(directory)).entryInfoList(nameFilters, QDir::Files | QDir::Executable); - for (const QFileInfo &binaryFi : binaries) { - const QString binary = binaryFi.fileName(); - if (!binary.contains(QLatin1String(webEngineProcessC), Qt::CaseInsensitive)) { - return binaryFi.absoluteFilePath(); - } - } - return QString(); -} - -static QString msgFileDoesNotExist(const QString & file) -{ - return QLatin1Char('"') + QDir::toNativeSeparators(file) - + QStringLiteral("\" does not exist."); -} - -enum CommandLineParseFlag { - CommandLineParseError = 0x1, - CommandLineParseHelpRequested = 0x2 -}; - -static inline int parseArguments(const QStringList &arguments, QCommandLineParser *parser, - Options *options, QString *errorMessage) -{ - using CommandLineOptionPtr = QSharedPointer<QCommandLineOption>; - using OptionPtrVector = QList<CommandLineOptionPtr>; - - parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); - parser->setApplicationDescription(QStringLiteral("Qt Deploy Tool ") + QLatin1String(QT_VERSION_STR) - + QLatin1String("\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n" - "installation (e.g. <QT_DIR\\bin>) to the PATH variable and then run:\n windeployqt <path-to-app-binary>\n\n" - "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>")); - const QCommandLineOption helpOption = parser->addHelpOption(); - parser->addVersionOption(); - - QCommandLineOption dirOption(QStringLiteral("dir"), - QStringLiteral("Use directory instead of binary directory."), - QStringLiteral("directory")); - parser->addOption(dirOption); - - QCommandLineOption qmakeOption(QStringLiteral("qmake"), - QStringLiteral("Use specified qmake instead of qmake from PATH. " - "Deprecated, use qtpaths instead."), - QStringLiteral("path")); - parser->addOption(qmakeOption); - - QCommandLineOption qtpathsOption( - QStringLiteral("qtpaths"), - QStringLiteral("Use specified qtpaths.exe instead of qtpaths.exe from PATH."), - QStringLiteral("path")); - parser->addOption(qtpathsOption); - - QCommandLineOption libDirOption(QStringLiteral("libdir"), - QStringLiteral("Copy libraries to path."), - QStringLiteral("path")); - parser->addOption(libDirOption); - - QCommandLineOption pluginDirOption(QStringLiteral("plugindir"), - QStringLiteral("Copy plugins to path."), - QStringLiteral("path")); - parser->addOption(pluginDirOption); - - QCommandLineOption debugOption(QStringLiteral("debug"), - QStringLiteral("Assume debug binaries.")); - parser->addOption(debugOption); - QCommandLineOption releaseOption(QStringLiteral("release"), - QStringLiteral("Assume release binaries.")); - parser->addOption(releaseOption); - QCommandLineOption releaseWithDebugInfoOption(QStringLiteral("release-with-debug-info"), - QStringLiteral("Assume release binaries with debug information.")); - releaseWithDebugInfoOption.setFlags(QCommandLineOption::HiddenFromHelp); // Deprecated by improved debug detection. - parser->addOption(releaseWithDebugInfoOption); - - QCommandLineOption deployPdbOption(QStringLiteral("pdb"), - QStringLiteral("Deploy .pdb files (MSVC).")); - parser->addOption(deployPdbOption); - - QCommandLineOption forceOption(QStringLiteral("force"), - QStringLiteral("Force updating files.")); - parser->addOption(forceOption); - - QCommandLineOption dryRunOption(QStringLiteral("dry-run"), - QStringLiteral("Simulation mode. Behave normally, but do not copy/update any files.")); - parser->addOption(dryRunOption); - - QCommandLineOption noPatchQtOption(QStringLiteral("no-patchqt"), - QStringLiteral("Do not patch the Qt6Core library.")); - parser->addOption(noPatchQtOption); - - QCommandLineOption ignoreErrorOption(QStringLiteral("ignore-library-errors"), - QStringLiteral("Ignore errors when libraries cannot be found.")); - parser->addOption(ignoreErrorOption); - - QCommandLineOption noPluginsOption(QStringLiteral("no-plugins"), - QStringLiteral("Skip plugin deployment.")); - parser->addOption(noPluginsOption); - - QCommandLineOption noLibraryOption(QStringLiteral("no-libraries"), - QStringLiteral("Skip library deployment.")); - parser->addOption(noLibraryOption); - - QCommandLineOption qmlDirOption(QStringLiteral("qmldir"), - QStringLiteral("Scan for QML-imports starting from directory."), - QStringLiteral("directory")); - parser->addOption(qmlDirOption); - - QCommandLineOption qmlImportOption(QStringLiteral("qmlimport"), - QStringLiteral("Add the given path to the QML module search locations."), - QStringLiteral("directory")); - parser->addOption(qmlImportOption); - - QCommandLineOption noQuickImportOption(QStringLiteral("no-quick-import"), - QStringLiteral("Skip deployment of Qt Quick imports.")); - parser->addOption(noQuickImportOption); - - - QCommandLineOption translationOption(QStringLiteral("translations"), - QStringLiteral("A comma-separated list of languages to deploy (de,fi)."), - QStringLiteral("languages")); - parser->addOption(translationOption); - - QCommandLineOption noTranslationOption(QStringLiteral("no-translations"), - QStringLiteral("Skip deployment of translations.")); - parser->addOption(noTranslationOption); - - QCommandLineOption noSystemD3DCompilerOption(QStringLiteral("no-system-d3d-compiler"), - QStringLiteral("Skip deployment of the system D3D compiler.")); - parser->addOption(noSystemD3DCompilerOption); - - - QCommandLineOption compilerRunTimeOption(QStringLiteral("compiler-runtime"), - QStringLiteral("Deploy compiler runtime (Desktop only).")); - parser->addOption(compilerRunTimeOption); - - QCommandLineOption noVirtualKeyboardOption(QStringLiteral("no-virtualkeyboard"), - QStringLiteral("Disable deployment of the Virtual Keyboard.")); - parser->addOption(noVirtualKeyboardOption); - - QCommandLineOption noCompilerRunTimeOption(QStringLiteral("no-compiler-runtime"), - QStringLiteral("Do not deploy compiler runtime (Desktop only).")); - parser->addOption(noCompilerRunTimeOption); - - QCommandLineOption jsonOption(QStringLiteral("json"), - QStringLiteral("Print to stdout in JSON format.")); - parser->addOption(jsonOption); - - QCommandLineOption suppressSoftwareRasterizerOption(QStringLiteral("no-opengl-sw"), - QStringLiteral("Do not deploy the software rasterizer library.")); - parser->addOption(suppressSoftwareRasterizerOption); - - QCommandLineOption listOption(QStringLiteral("list"), - QLatin1String("Print only the names of the files copied.\n" - "Available options:\n" - " source: absolute path of the source files\n" - " target: absolute path of the target files\n" - " relative: paths of the target files, relative\n" - " to the target directory\n" - " mapping: outputs the source and the relative\n" - " target, suitable for use within an\n" - " Appx mapping file"), - QStringLiteral("option")); - parser->addOption(listOption); - - QCommandLineOption verboseOption(QStringLiteral("verbose"), - QStringLiteral("Verbose level (0-2)."), - QStringLiteral("level")); - parser->addOption(verboseOption); - - parser->addPositionalArgument(QStringLiteral("[files]"), - QStringLiteral("Binaries or directory containing the binary.")); - - OptionPtrVector enabledModuleOptions; - OptionPtrVector disabledModuleOptions; - const int qtModulesCount = int(sizeof(qtModuleEntries) / sizeof(QtModuleEntry)); - enabledModuleOptions.reserve(qtModulesCount); - disabledModuleOptions.reserve(qtModulesCount); - for (int i = 0; i < qtModulesCount; ++i) { - const QString option = QLatin1String(qtModuleEntries[i].option); - const QString name = QLatin1String(qtModuleEntries[i].libraryName); - const QString enabledDescription = QStringLiteral("Add ") + name + QStringLiteral(" module."); - CommandLineOptionPtr enabledOption(new QCommandLineOption(option, enabledDescription)); - parser->addOption(*enabledOption.data()); - enabledModuleOptions.append(enabledOption); - const QString disabledDescription = QStringLiteral("Remove ") + name + QStringLiteral(" module."); - CommandLineOptionPtr disabledOption(new QCommandLineOption(QStringLiteral("no-") + option, - disabledDescription)); - disabledModuleOptions.append(disabledOption); - parser->addOption(*disabledOption.data()); - } - - const bool success = parser->parse(arguments); - if (parser->isSet(helpOption)) - return CommandLineParseHelpRequested; - if (!success) { - *errorMessage = parser->errorText(); - return CommandLineParseError; - } - - options->libraryDirectory = parser->value(libDirOption); - options->pluginDirectory = parser->value(pluginDirOption); - options->plugins = !parser->isSet(noPluginsOption); - options->libraries = !parser->isSet(noLibraryOption); - options->translations = !parser->isSet(noTranslationOption); - if (parser->isSet(translationOption)) - options->languages = parser->value(translationOption).split(QLatin1Char(',')); - options->systemD3dCompiler = !parser->isSet(noSystemD3DCompilerOption); - options->quickImports = !parser->isSet(noQuickImportOption); - - // default to deployment of compiler runtime for windows desktop configurations - if (options->platform == WindowsDesktopMinGW || options->platform == WindowsDesktopMsvc - || parser->isSet(compilerRunTimeOption)) - options->compilerRunTime = true; - if (parser->isSet(noCompilerRunTimeOption)) - options->compilerRunTime = false; - - if (options->compilerRunTime && options->platform != WindowsDesktopMinGW && options->platform != WindowsDesktopMsvc) { - *errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for Desktop MSVC/g++ only."); - return CommandLineParseError; - } - - if (parser->isSet(noVirtualKeyboardOption)) - options->disabledPlugins |= QtVirtualKeyboardPlugin; - - if (parser->isSet(releaseWithDebugInfoOption)) - std::wcerr << "Warning: " << releaseWithDebugInfoOption.names().first() << " is obsolete."; - - switch (parseExclusiveOptions(parser, debugOption, releaseOption)) { - case OptionAuto: - break; - case OptionEnabled: - options->debugDetection = Options::DebugDetectionForceDebug; - break; - case OptionDisabled: - options->debugDetection = Options::DebugDetectionForceRelease; - break; - } - - if (parser->isSet(deployPdbOption)) { - if (options->platform.testFlag(WindowsBased) && !options->platform.testFlag(MinGW)) - options->deployPdb = true; - else - std::wcerr << "Warning: --" << deployPdbOption.names().first() << " is not supported on this platform.\n"; - } - - if (parser->isSet(suppressSoftwareRasterizerOption)) - options->softwareRasterizer = false; - - if (parser->isSet(forceOption)) - options->updateFileFlags |= ForceUpdateFile; - if (parser->isSet(dryRunOption)) { - options->dryRun = true; - options->updateFileFlags |= SkipUpdateFile; - } - - options->patchQt = !parser->isSet(noPatchQtOption); - options->ignoreLibraryErrors = parser->isSet(ignoreErrorOption); - - for (int i = 0; i < qtModulesCount; ++i) { - if (parser->isSet(*enabledModuleOptions.at(i))) - options->additionalLibraries |= qtModuleEntries[i].module; - if (parser->isSet(*disabledModuleOptions.at(i))) - options->disabledLibraries |= qtModuleEntries[i].module; - } - - // Add some dependencies - if (options->additionalLibraries & QtQuickModule) - options->additionalLibraries |= QtQmlModule; - if (options->additionalLibraries & QtDesignerComponents) - options->additionalLibraries |= QtDesignerModule; - - if (parser->isSet(listOption)) { - const QString value = parser->value(listOption); - if (value == QStringLiteral("source")) { - options->list = ListSource; - } else if (value == QStringLiteral("target")) { - options->list = ListTarget; - } else if (value == QStringLiteral("relative")) { - options->list = ListRelative; - } else if (value == QStringLiteral("mapping")) { - options->list = ListMapping; - } else { - *errorMessage = QStringLiteral("Please specify a valid option for -list (source, target, relative, mapping)."); - return CommandLineParseError; - } - } - - if (parser->isSet(jsonOption) || options->list) { - optVerboseLevel = 0; - options->json = new JsonOutput; - } else { - if (parser->isSet(verboseOption)) { - bool ok; - const QString value = parser->value(verboseOption); - optVerboseLevel = value.toInt(&ok); - if (!ok || optVerboseLevel < 0) { - *errorMessage = QStringLiteral("Invalid value \"%1\" passed for verbose level.").arg(value); - return CommandLineParseError; - } - } - } - - const QStringList posArgs = parser->positionalArguments(); - if (posArgs.isEmpty()) { - *errorMessage = QStringLiteral("Please specify the binary or folder."); - return CommandLineParseError | CommandLineParseHelpRequested; - } - - if (parser->isSet(dirOption)) - options->directory = parser->value(dirOption); - - if (parser->isSet(qmakeOption) && parser->isSet(qtpathsOption)) { - *errorMessage = QStringLiteral("-qmake and -qtpaths are mutually exclusive."); - return CommandLineParseError; - } - - if (parser->isSet(qmakeOption) && optVerboseLevel >= 1) - std::wcerr << "Warning: -qmake option is deprecated. Use -qpaths instead.\n"; - - if (parser->isSet(qtpathsOption) || parser->isSet(qmakeOption)) { - const QString qtpathsArg = parser->isSet(qtpathsOption) ? parser->value(qtpathsOption) - : parser->value(qmakeOption); - - const QString qtpathsBinary = QDir::cleanPath(qtpathsArg); - const QFileInfo fi(qtpathsBinary); - if (!fi.exists()) { - *errorMessage = msgFileDoesNotExist(qtpathsBinary); - return CommandLineParseError; - } - - if (!fi.isExecutable()) { - *errorMessage = QLatin1Char('"') + QDir::toNativeSeparators(qtpathsBinary) - + QStringLiteral("\" is not an executable."); - return CommandLineParseError; - } - options->qtpathsBinary = qtpathsBinary; - } - - if (parser->isSet(qmlDirOption)) - options->qmlDirectories = parser->values(qmlDirOption); - - if (parser->isSet(qmlImportOption)) - options->qmlImportPaths = parser->values(qmlImportOption); - - const QString &file = posArgs.front(); - const QFileInfo fi(QDir::cleanPath(file)); - if (!fi.exists()) { - *errorMessage = msgFileDoesNotExist(file); - return CommandLineParseError; - } - - if (!options->directory.isEmpty() && !fi.isFile()) { // -dir was specified - expecting file. - *errorMessage = QLatin1Char('"') + file + QStringLiteral("\" is not an executable file."); - return CommandLineParseError; - } - - if (fi.isFile()) { - options->binaries.append(fi.absoluteFilePath()); - if (options->directory.isEmpty()) - options->directory = fi.absolutePath(); - } else { - const QString binary = findBinary(fi.absoluteFilePath(), options->platform); - if (binary.isEmpty()) { - *errorMessage = QStringLiteral("Unable to find binary in \"") + file + QLatin1Char('"'); - return CommandLineParseError; - } - options->directory = fi.absoluteFilePath(); - options->binaries.append(binary); - } // directory. - - // Remaining files or plugin directories - for (int i = 1; i < posArgs.size(); ++i) { - const QFileInfo fi(QDir::cleanPath(posArgs.at(i))); - const QString path = fi.absoluteFilePath(); - if (!fi.exists()) { - *errorMessage = msgFileDoesNotExist(path); - return CommandLineParseError; - } - if (fi.isDir()) { - const QStringList libraries = - findSharedLibraries(QDir(path), options->platform, MatchDebugOrRelease, QString()); - for (const QString &library : libraries) - options->binaries.append(path + QLatin1Char('/') + library); - } else { - options->binaries.append(path); - } - } - options->translationsDirectory = options->directory + QLatin1String("/translations"); - return 0; -} - -// Simple line wrapping at 80 character boundaries. -static inline QString lineBreak(QString s) -{ - for (int i = 80; i < s.size(); i += 80) { - const int lastBlank = s.lastIndexOf(QLatin1Char(' '), i); - if (lastBlank >= 0) { - s[lastBlank] = QLatin1Char('\n'); - i = lastBlank + 1; - } - } - return s; -} - -static inline QString helpText(const QCommandLineParser &p) -{ - QString result = p.helpText(); - // Replace the default-generated text which is too long by a short summary - // explaining how to enable single libraries. - const int moduleStart = result.indexOf(QLatin1String("\n --bluetooth")); - const int argumentsStart = result.lastIndexOf(QLatin1String("\nArguments:")); - if (moduleStart >= argumentsStart) - return result; - QString moduleHelp = QLatin1String( - "\n\nQt libraries can be added by passing their name (-xml) or removed by passing\n" - "the name prepended by --no- (--no-xml). Available libraries:\n"); - moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(0xFFFFFFFFFFFFFFFFull, true))); - moduleHelp += QLatin1Char('\n'); - result.replace(moduleStart, argumentsStart - moduleStart, moduleHelp); - return result; -} - -static inline bool isQtModule(const QString &libName) -{ - // Match Standard modules named Qt6XX.dll - if (libName.size() < 3 || !libName.startsWith(QLatin1String("Qt"), Qt::CaseInsensitive)) - return false; - const QChar version = libName.at(2); - return version.isDigit() && (version.toLatin1() - '0') == QT_VERSION_MAJOR; -} - -// Helper for recursively finding all dependent Qt libraries. -static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary, Platform platform, - QString *errorMessage, QStringList *result, - unsigned *wordSize = nullptr, bool *isDebug = nullptr, - unsigned short *machineArch = nullptr, - int *directDependencyCount = nullptr, int recursionDepth = 0) -{ - QStringList dependentLibs; - if (directDependencyCount) - *directDependencyCount = 0; - if (!readExecutable(binary, platform, errorMessage, &dependentLibs, wordSize, isDebug, machineArch)) { - errorMessage->prepend(QLatin1String("Unable to find dependent libraries of ") + - QDir::toNativeSeparators(binary) + QLatin1String(" :")); - return false; - } - // Filter out the Qt libraries. Note that depends.exe finds libs from optDirectory if we - // are run the 2nd time (updating). We want to check against the Qt bin dir libraries - const int start = result->size(); - for (const QString &lib : qAsConst(dependentLibs)) { - if (isQtModule(lib)) { - const QString path = normalizeFileName(qtBinDir + QLatin1Char('/') + QFileInfo(lib).fileName()); - if (!result->contains(path)) - result->append(path); - } - } - const int end = result->size(); - if (directDependencyCount) - *directDependencyCount = end - start; - // Recurse - for (int i = start; i < end; ++i) - if (!findDependentQtLibraries(qtBinDir, result->at(i), platform, errorMessage, result, - nullptr, nullptr, nullptr, nullptr, recursionDepth + 1)) - return false; - return true; -} - -// Base class to filter debug/release Windows DLLs for functions to be passed to updateFile(). -// Tries to pre-filter by namefilter and does check via PE. -class DllDirectoryFileEntryFunction { -public: - explicit DllDirectoryFileEntryFunction(Platform platform, - DebugMatchMode debugMatchMode, const QString &prefix = QString()) : - m_platform(platform), m_debugMatchMode(debugMatchMode), m_prefix(prefix) {} - - QStringList operator()(const QDir &dir) const - { return findSharedLibraries(dir, m_platform, m_debugMatchMode, m_prefix); } - -private: - const Platform m_platform; - const DebugMatchMode m_debugMatchMode; - const QString m_prefix; -}; - -static QString pdbFileName(QString libraryFileName) -{ - const int lastDot = libraryFileName.lastIndexOf(QLatin1Char('.')) + 1; - if (lastDot <= 0) - return QString(); - libraryFileName.replace(lastDot, libraryFileName.size() - lastDot, QLatin1String("pdb")); - return libraryFileName; -} -static inline QStringList qmlCacheFileFilters() -{ - return QStringList() << QStringLiteral("*.jsc") << QStringLiteral("*.qmlc"); -} - -// File entry filter function for updateFile() that returns a list of files for -// QML import trees: DLLs (matching debgug) and .qml/,js, etc. -class QmlDirectoryFileEntryFunction { -public: - enum Flags { - DeployPdb = 0x1, - SkipSources = 0x2 - }; - - explicit QmlDirectoryFileEntryFunction(Platform platform, DebugMatchMode debugMatchMode, unsigned flags) - : m_flags(flags), m_qmlNameFilter(QmlDirectoryFileEntryFunction::qmlNameFilters(flags)) - , m_dllFilter(platform, debugMatchMode) - {} - - QStringList operator()(const QDir &dir) const - { - QStringList result; - const QStringList &libraries = m_dllFilter(dir); - for (const QString &library : libraries) { - result.append(library); - if (m_flags & DeployPdb) { - const QString pdb = pdbFileName(library); - if (QFileInfo(dir.absoluteFilePath(pdb)).isFile()) - result.append(pdb); - } - } - result.append(m_qmlNameFilter(dir)); - return result; - } - -private: - static inline QStringList qmlNameFilters(unsigned flags) - { - QStringList result; - result << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes") - << QStringLiteral("*.frag") << QStringLiteral("*.vert") // Shaders - << QStringLiteral("*.ttf"); - if (!(flags & SkipSources)) { - result << QStringLiteral("*.js") << QStringLiteral("*.qml") << QStringLiteral("*.png"); - result.append(qmlCacheFileFilters()); - } - return result; - } - - const unsigned m_flags; - NameFilterFileEntryFunction m_qmlNameFilter; - DllDirectoryFileEntryFunction m_dllFilter; -}; - -struct PluginModuleMapping -{ - const char *directoryName; - quint64 module; -}; - -static const PluginModuleMapping pluginModuleMappings[] = -{ - {"qml1tooling", QtDeclarativeModule}, -#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) - {"gamepads", QtGamePadModule}, -#endif - {"accessible", QtGuiModule}, - {"iconengines", QtGuiModule}, - {"imageformats", QtGuiModule}, - {"platforms", QtGuiModule}, - {"platforminputcontexts", QtGuiModule}, - {"virtualkeyboard", QtGuiModule}, - {"geoservices", QtLocationModule}, - {"audio", QtMultimediaModule}, - {"mediaservice", QtMultimediaModule}, - {"playlistformats", QtMultimediaModule}, - {"networkaccess", QtNetworkModule}, - {"networkinformation", QtNetworkModule}, - {"tls", QtNetworkModule}, - {"position", QtPositioningModule}, - {"printsupport", QtPrintSupportModule}, - {"scenegraph", QtQuickModule}, - {"qmltooling", QtQuickModule | QtQmlToolingModule}, - {"sensors", QtSensorsModule}, - {"sensorgestures", QtSensorsModule}, - {"canbus", QtSerialBusModule}, - {"sqldrivers", QtSqlModule}, -#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) - {"texttospeech", QtTextToSpeechModule}, -#endif - {"qtwebengine", QtWebEngineModule | QtWebEngineCoreModule | QtWebEngineWidgetsModule}, - {"styles", QtWidgetsModule}, - {"sceneparsers", Qt3DRendererModule}, - {"renderers", Qt3DRendererModule | QtShaderToolsModule}, - {"renderplugins", Qt3DRendererModule}, - {"geometryloaders", Qt3DRendererModule}, - {"webview", QtWebViewModule} -}; - -static inline quint64 qtModuleForPlugin(const QString &subDirName) -{ - const auto end = std::end(pluginModuleMappings); - const auto result = - std::find_if(std::begin(pluginModuleMappings), end, - [&subDirName] (const PluginModuleMapping &m) { return subDirName == QLatin1String(m.directoryName); }); - return result != end ? result->module : 0; // "designer" -} - -static quint64 qtModule(QString module, const QString &infix) -{ - // Match needle 'path/Qt6Core<infix><d>.dll' or 'path/libQt6Core<infix>.so.5.0' - const int lastSlashPos = module.lastIndexOf(QLatin1Char('/')); - if (lastSlashPos > 0) - module.remove(0, lastSlashPos + 1); - if (module.startsWith(QLatin1String("lib"))) - module.remove(0, 3); - int endPos = infix.isEmpty() ? -1 : module.lastIndexOf(infix); - if (endPos == -1) - endPos = module.indexOf(QLatin1Char('.')); // strip suffixes '.so.5.0'. - if (endPos > 0) - module.truncate(endPos); - // That should leave us with 'Qt6Core<d>'. - for (const auto &qtModule : qtModuleEntries) { - const QLatin1String libraryName(qtModule.libraryName); - if (module == libraryName - || (module.size() == libraryName.size() + 1 && module.startsWith(libraryName))) { - return qtModule.module; - } - } - return 0; -} - -QStringList findQtPlugins(quint64 *usedQtModules, quint64 disabledQtModules, - unsigned disabledPlugins, - const QString &qtPluginsDirName, const QString &libraryLocation, - const QString &infix, - DebugMatchMode debugMatchModeIn, Platform platform, QString *platformPlugin) -{ - QString errorMessage; - if (qtPluginsDirName.isEmpty()) - return QStringList(); - QDir pluginsDir(qtPluginsDirName); - QStringList result; - const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(QLatin1String("*")), QDir::Dirs | QDir::NoDotAndDotDot); - for (const QFileInfo &subDirFi : pluginDirs) { - const QString subDirName = subDirFi.fileName(); - const quint64 module = qtModuleForPlugin(subDirName); - if (module & *usedQtModules) { - const DebugMatchMode debugMatchMode = (module & QtWebEngineCoreModule) - ? MatchDebugOrRelease // QTBUG-44331: Debug detection does not work for webengine, deploy all. - : debugMatchModeIn; - QDir subDir(subDirFi.absoluteFilePath()); - // Filter out disabled plugins - if ((disabledPlugins & QtVirtualKeyboardPlugin) && subDirName == QLatin1String("virtualkeyboard")) - continue; - if (disabledQtModules & QtQmlToolingModule && subDirName == QLatin1String("qmltooling")) - continue; - // Filter for platform or any. - QString filter; - const bool isPlatformPlugin = subDirName == QLatin1String("platforms"); - if (isPlatformPlugin) { - switch (platform) { - case WindowsDesktopMsvc: - case WindowsDesktopMinGW: - filter = QStringLiteral("qwindows"); - break; - case Unix: - filter = QStringLiteral("libqxcb"); - break; - case UnknownPlatform: - break; - } - } else { - filter = QLatin1String("*"); - } - const QStringList plugins = findSharedLibraries(subDir, platform, debugMatchMode, filter); - for (const QString &plugin : plugins) { - // Filter out disabled plugins - if ((disabledPlugins & QtVirtualKeyboardPlugin) - && plugin.startsWith(QLatin1String("qtvirtualkeyboardplugin"))) { - continue; - } - const QString pluginPath = subDir.absoluteFilePath(plugin); - if (isPlatformPlugin) - *platformPlugin = pluginPath; - QStringList dependentQtLibs; - quint64 neededModules = 0; - if (findDependentQtLibraries(libraryLocation, pluginPath, platform, &errorMessage, &dependentQtLibs)) { - for (int d = 0; d < dependentQtLibs.size(); ++ d) - neededModules |= qtModule(dependentQtLibs.at(d), infix); - } else { - std::wcerr << "Warning: Cannot determine dependencies of " - << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n'; - } - if (const quint64 missingModules = neededModules & disabledQtModules) { - if (optVerboseLevel) { - std::wcout << "Skipping plugin " << plugin - << " due to disabled dependencies (" - << formatQtModules(missingModules).constData() << ").\n"; - } - } else { - if (const quint64 missingModules = (neededModules & ~*usedQtModules)) { - *usedQtModules |= missingModules; - if (optVerboseLevel) - std::wcout << "Adding " << formatQtModules(missingModules).constData() << " for " << plugin << '\n'; - } - result.append(pluginPath); - } - } // for filter - } // type matches - } // for plugin folder - return result; -} - -static QStringList translationNameFilters(quint64 modules, const QString &prefix) -{ - QStringList result; - for (const auto &qtModule : qtModuleEntries) { - if ((qtModule.module & modules) && qtModule.translation) { - const QString name = QLatin1String(qtModule.translation) + - QLatin1Char('_') + prefix + QStringLiteral(".qm"); - if (!result.contains(name)) - result.push_back(name); - } - } - return result; -} - -static bool deployTranslations(const QString &sourcePath, quint64 usedQtModules, - const QString &target, const Options &options, - QString *errorMessage) -{ - // Find available languages prefixes by checking on qtbase. - QStringList prefixes; - QDir sourceDir(sourcePath); - const QStringList qmFilter = QStringList(QStringLiteral("qtbase_*.qm")); - const QFileInfoList &qmFiles = sourceDir.entryInfoList(qmFilter); - for (const QFileInfo &qmFi : qmFiles) { - const QString prefix = qmFi.baseName().mid(7); - if (options.languages.isEmpty() || options.languages.contains(prefix)) - prefixes.append(prefix); - } - if (prefixes.isEmpty()) { - std::wcerr << "Warning: Could not find any translations in " - << QDir::toNativeSeparators(sourcePath) << " (developer build?)\n."; - return true; - } - // Run lconvert to concatenate all files into a single named "qt_<prefix>.qm" in the application folder - // Use QT_INSTALL_TRANSLATIONS as working directory to keep the command line short. - const QString absoluteTarget = QFileInfo(target).absoluteFilePath(); - const QString binary = QStringLiteral("lconvert"); - QStringList arguments; - for (const QString &prefix : qAsConst(prefixes)) { - arguments.clear(); - const QString targetFile = QStringLiteral("qt_") + prefix + QStringLiteral(".qm"); - arguments.append(QStringLiteral("-o")); - const QString targetFilePath = absoluteTarget + QLatin1Char('/') + targetFile; - if (options.json) - options.json->addFile(sourcePath + QLatin1Char('/') + targetFile, absoluteTarget); - arguments.append(QDir::toNativeSeparators(targetFilePath)); - const QFileInfoList &langQmFiles = sourceDir.entryInfoList(translationNameFilters(usedQtModules, prefix)); - for (const QFileInfo &langQmFileFi : langQmFiles) { - if (options.json) { - options.json->addFile(langQmFileFi.absoluteFilePath(), - absoluteTarget); - } - arguments.append(langQmFileFi.fileName()); - } - if (optVerboseLevel) - std::wcout << "Creating " << targetFile << "...\n"; - unsigned long exitCode; - if ((options.updateFileFlags & SkipUpdateFile) == 0 - && (!runProcess(binary, arguments, sourcePath, &exitCode, nullptr, nullptr, errorMessage) - || exitCode)) { - return false; - } - } // for prefixes. - return true; -} - -struct DeployResult -{ - operator bool() const { return success; } - - bool success = false; - bool isDebug = false; - quint64 directlyUsedQtLibraries = 0; - quint64 usedQtLibraries = 0; - quint64 deployedQtLibraries = 0; -}; - -static QString libraryPath(const QString &libraryLocation, const char *name, - const QString &qtLibInfix, Platform platform, bool debug) -{ - QString result = libraryLocation + QLatin1Char('/'); - if (platform & WindowsBased) { - result += QLatin1String(name); - result += qtLibInfix; - if (debug && platformHasDebugSuffix(platform)) - result += QLatin1Char('d'); - } else if (platform.testFlag(UnixBased)) { - result += QStringLiteral("lib"); - result += QLatin1String(name); - result += qtLibInfix; - } - result += sharedLibrarySuffix(platform); - return result; -} - -static QString vcDebugRedistDir() { return QStringLiteral("Debug_NonRedist"); } - -static QString vcRedistDir() -{ - const char vcDirVar[] = "VCINSTALLDIR"; - const QChar slash(QLatin1Char('/')); - QString vcRedistDirName = QDir::cleanPath(QFile::decodeName(qgetenv(vcDirVar))); - if (vcRedistDirName.isEmpty()) { - std::wcerr << "Warning: Cannot find Visual Studio installation directory, " << vcDirVar - << " is not set.\n"; - return QString(); - } - if (!vcRedistDirName.endsWith(slash)) - vcRedistDirName.append(slash); - vcRedistDirName.append(QStringLiteral("redist/MSVC")); - if (!QFileInfo(vcRedistDirName).isDir()) { - std::wcerr << "Warning: Cannot find Visual Studio redist directory, " - << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n"; - return QString(); - } - // Look in reverse order for folder containing the debug redist folder - const QFileInfoList subDirs = - QDir(vcRedistDirName) - .entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); - for (const QFileInfo &f : subDirs) { - QString path = f.absoluteFilePath(); - if (QFileInfo(path + slash + vcDebugRedistDir()).isDir()) - return path; - path += QStringLiteral("/onecore"); - if (QFileInfo(path + slash + vcDebugRedistDir()).isDir()) - return path; - } - std::wcerr << "Warning: Cannot find Visual Studio redist directory under " - << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n"; - return QString(); -} - -static QStringList compilerRunTimeLibs(Platform platform, bool isDebug, unsigned short machineArch) -{ - QStringList result; - switch (platform) { - case WindowsDesktopMinGW: { // MinGW: Add runtime libraries - static const char *minGwRuntimes[] = {"*gcc_", "*stdc++", "*winpthread"}; - const QString gcc = findInPath(QStringLiteral("g++.exe")); - if (gcc.isEmpty()) { - std::wcerr << "Warning: Cannot find GCC installation directory. g++.exe must be in the path.\n"; - break; - } - const QString binPath = QFileInfo(gcc).absolutePath(); - QStringList filters; - const QString suffix = QLatin1Char('*') + sharedLibrarySuffix(platform); - for (auto minGwRuntime : minGwRuntimes) - filters.append(QLatin1String(minGwRuntime) + suffix); - const QFileInfoList &dlls = QDir(binPath).entryInfoList(filters, QDir::Files); - for (const QFileInfo &dllFi : dlls) - result.append(dllFi.absoluteFilePath()); - } - break; -#ifdef Q_OS_WIN - case WindowsDesktopMsvc: { // MSVC/Desktop: Add redistributable packages. - QString vcRedistDirName = vcRedistDir(); - if (vcRedistDirName.isEmpty()) - break; - QStringList redistFiles; - QDir vcRedistDir(vcRedistDirName); - const QString machineArchString = getArchString(machineArch); - if (isDebug) { - // Append DLLs from Debug_NonRedist\x??\Microsoft.VC<version>.DebugCRT. - if (vcRedistDir.cd(vcDebugRedistDir()) && vcRedistDir.cd(machineArchString)) { - const QStringList names = vcRedistDir.entryList(QStringList(QStringLiteral("Microsoft.VC*.DebugCRT")), QDir::Dirs); - if (!names.isEmpty() && vcRedistDir.cd(names.first())) { - const QFileInfoList &dlls = vcRedistDir.entryInfoList(QStringList(QLatin1String("*.dll"))); - for (const QFileInfo &dll : dlls) - redistFiles.append(dll.absoluteFilePath()); - } - } - } else { // release: Bundle vcredist<>.exe - QString releaseRedistDir = vcRedistDirName; - const QStringList countryCodes = vcRedistDir.entryList(QStringList(QStringLiteral("[0-9]*")), QDir::Dirs); - if (!countryCodes.isEmpty()) // Pre MSVC2017 - releaseRedistDir += QLatin1Char('/') + countryCodes.constFirst(); - QFileInfo fi(releaseRedistDir + QLatin1Char('/') + QStringLiteral("vc_redist.") - + machineArchString + QStringLiteral(".exe")); - if (!fi.isFile()) { // Pre MSVC2017/15.5 - fi.setFile(releaseRedistDir + QLatin1Char('/') + QStringLiteral("vcredist_") - + machineArchString + QStringLiteral(".exe")); - } - if (fi.isFile()) - redistFiles.append(fi.absoluteFilePath()); - } - if (redistFiles.isEmpty()) { - std::wcerr << "Warning: Cannot find Visual Studio " << (isDebug ? "debug" : "release") - << " redistributable files in " << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n"; - break; - } - result.append(redistFiles); - } - break; -#endif // Q_OS_WIN - default: - break; - } - return result; -} - -static inline int qtVersion(const QMap<QString, QString> &qtpathsVariables) -{ - const QString versionString = qtpathsVariables.value(QStringLiteral("QT_VERSION")); - const QChar dot = QLatin1Char('.'); - const int majorVersion = versionString.section(dot, 0, 0).toInt(); - const int minorVersion = versionString.section(dot, 1, 1).toInt(); - const int patchVersion = versionString.section(dot, 2, 2).toInt(); - return (majorVersion << 16) | (minorVersion << 8) | patchVersion; -} - -// Determine the Qt lib infix from the library path of "Qt6Core<qtblibinfix>[d].dll". -static inline QString qtlibInfixFromCoreLibName(const QString &path, bool isDebug, Platform platform) -{ - const int startPos = path.lastIndexOf(QLatin1Char('/')) + 8; - int endPos = path.lastIndexOf(QLatin1Char('.')); - if (isDebug && (platform & WindowsBased)) - endPos--; - return endPos > startPos ? path.mid(startPos, endPos - startPos) : QString(); -} - -// Deploy a library along with its .pdb debug info file (MSVC) should it exist. -static bool updateLibrary(const QString &sourceFileName, const QString &targetDirectory, - const Options &options, QString *errorMessage) -{ - if (!updateFile(sourceFileName, targetDirectory, options.updateFileFlags, options.json, errorMessage)) { - if (options.ignoreLibraryErrors) { - std::wcerr << "Warning: Could not update " << sourceFileName << " :" << *errorMessage << '\n'; - errorMessage->clear(); - return true; - } - return false; - } - - if (options.deployPdb) { - const QFileInfo pdb(pdbFileName(sourceFileName)); - if (pdb.isFile()) - return updateFile(pdb.absoluteFilePath(), targetDirectory, options.updateFileFlags, nullptr, errorMessage); - } - return true; -} - -// Find out the ICU version to add the data library icudtXX.dll, which does not -// show as a dependency. -static QString getIcuVersion(const QString &libName) -{ - QString version; - std::copy_if(libName.cbegin(), libName.cend(), std::back_inserter(version), - [](QChar c) { return c.isDigit(); }); - return version; -} - -static DeployResult deploy(const Options &options, const QMap<QString, QString> &qtpathsVariables, - QString *errorMessage) -{ - DeployResult result; - - const QChar slash = QLatin1Char('/'); - - const QString qtBinDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_BINS")); - const QString libraryLocation = options.platform == Unix - ? qtpathsVariables.value(QStringLiteral("QT_INSTALL_LIBS")) - : qtBinDir; - const QString infix = qtpathsVariables.value(QLatin1String(qmakeInfixKey)); - const int version = qtVersion(qtpathsVariables); - Q_UNUSED(version); - - if (optVerboseLevel > 1) - std::wcout << "Qt binaries in " << QDir::toNativeSeparators(qtBinDir) << '\n'; - - QStringList dependentQtLibs; - bool detectedDebug; - unsigned wordSize; - unsigned short machineArch; - int directDependencyCount = 0; - if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform, errorMessage, &dependentQtLibs, &wordSize, - &detectedDebug, &machineArch, &directDependencyCount)) { - return result; - } - for (int b = 1; b < options.binaries.size(); ++b) { - if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform, errorMessage, &dependentQtLibs, - nullptr, nullptr, nullptr)) { - return result; - } - } - - DebugMatchMode debugMatchMode = MatchDebugOrRelease; - result.isDebug = false; - switch (options.debugDetection) { - case Options::DebugDetectionAuto: - // Debug detection is only relevant for Msvc/ClangMsvc which have distinct - // runtimes and binaries. For anything else, use MatchDebugOrRelease - // since also debug cannot be reliably detect for MinGW. - if (options.platform.testFlag(Msvc) || options.platform.testFlag(ClangMsvc)) { - result.isDebug = detectedDebug; - debugMatchMode = result.isDebug ? MatchDebug : MatchRelease; - } - break; - case Options::DebugDetectionForceDebug: - result.isDebug = true; - debugMatchMode = MatchDebug; - break; - case Options::DebugDetectionForceRelease: - debugMatchMode = MatchRelease; - break; - } - - // Determine application type, check Quick2 is used by looking at the - // direct dependencies (do not be fooled by QtWebKit depending on it). - QString qtLibInfix; - for (int m = 0; m < directDependencyCount; ++m) { - const quint64 module = qtModule(dependentQtLibs.at(m), infix); - result.directlyUsedQtLibraries |= module; - if (module == QtCoreModule) - qtLibInfix = qtlibInfixFromCoreLibName(dependentQtLibs.at(m), detectedDebug, options.platform); - } - - const bool usesQml2 = !(options.disabledLibraries & QtQmlModule) - && ((result.directlyUsedQtLibraries & (QtQmlModule | QtQuickModule | Qt3DQuickModule)) - || (options.additionalLibraries & QtQmlModule)); - - if (optVerboseLevel) { - std::wcout << QDir::toNativeSeparators(options.binaries.first()) << ' ' - << wordSize << " bit, " << (result.isDebug ? "debug" : "release") - << " executable"; - if (usesQml2) - std::wcout << " [QML]"; - std::wcout << '\n'; - } - - if (dependentQtLibs.isEmpty()) { - *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(" does not seem to be a Qt executable."); - return result; - } - - // Some Windows-specific checks: Qt5Core depends on ICU when configured with "-icu". Other than - // that, Qt5WebKit has a hard dependency on ICU. - if (options.platform.testFlag(WindowsBased)) { - const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral("Qt6Core"), Qt::CaseInsensitive) - + dependentQtLibs.filter(QStringLiteral("Qt5WebKit"), Qt::CaseInsensitive); - for (const QString &qtLib : qtLibs) { - QStringList icuLibs = findDependentLibraries(qtLib, options.platform, errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive); - if (!icuLibs.isEmpty()) { - // Find out the ICU version to add the data library icudtXX.dll, which does not show - // as a dependency. - const QString icuVersion = getIcuVersion(icuLibs.constFirst()); - if (!icuVersion.isEmpty()) { - if (optVerboseLevel > 1) - std::wcout << "Adding ICU version " << icuVersion << '\n'; - QString icuLib = QStringLiteral("icudt") + icuVersion - + QLatin1String(windowsSharedLibrarySuffix);; - // Some packages contain debug dlls of ICU libraries even though it's a C - // library and the official packages do not differentiate (QTBUG-87677) - if (result.isDebug) { - const QString icuLibCandidate = QStringLiteral("icudtd") + icuVersion - + QLatin1String(windowsSharedLibrarySuffix); - if (!findInPath(icuLibCandidate).isEmpty()) { - icuLib = icuLibCandidate; - } - } - icuLibs.push_back(icuLib); - } - for (const QString &icuLib : qAsConst(icuLibs)) { - const QString icuPath = findInPath(icuLib); - if (icuPath.isEmpty()) { - *errorMessage = QStringLiteral("Unable to locate ICU library ") + icuLib; - return result; - } - dependentQtLibs.push_back(icuPath); - } // for each icuLib - break; - } // !icuLibs.isEmpty() - } // Qt6Core/Qt6WebKit - } // Windows - - // Scan Quick2 imports - QmlImportScanResult qmlScanResult; - if (options.quickImports && usesQml2) { - // Custom list of import paths provided by user - QStringList qmlImportPaths = options.qmlImportPaths; - // Qt's own QML modules - qmlImportPaths << qtpathsVariables.value(QStringLiteral("QT_INSTALL_QML")); - QStringList qmlDirectories = options.qmlDirectories; - if (qmlDirectories.isEmpty()) { - const QString qmlDirectory = findQmlDirectory(options.platform, options.directory); - if (!qmlDirectory.isEmpty()) - qmlDirectories.append(qmlDirectory); - } - for (const QString &qmlDirectory : qAsConst(qmlDirectories)) { - if (optVerboseLevel >= 1) - std::wcout << "Scanning " << QDir::toNativeSeparators(qmlDirectory) << ":\n"; - const QmlImportScanResult scanResult = - runQmlImportScanner(qmlDirectory, qmlImportPaths, - result.directlyUsedQtLibraries & QtWidgetsModule, - options.platform, debugMatchMode, errorMessage); - if (!scanResult.ok) - return result; - qmlScanResult.append(scanResult); - // Additional dependencies of QML plugins. - for (const QString &plugin : qAsConst(qmlScanResult.plugins)) { - if (!findDependentQtLibraries(libraryLocation, plugin, options.platform, errorMessage, &dependentQtLibs, &wordSize, &detectedDebug, &machineArch)) - return result; - } - if (optVerboseLevel >= 1) { - std::wcout << "QML imports:\n"; - for (const QmlImportScanResult::Module &mod : qAsConst(qmlScanResult.modules)) { - std::wcout << " '" << mod.name << "' " - << QDir::toNativeSeparators(mod.sourcePath) << '\n'; - } - if (optVerboseLevel >= 2) { - std::wcout << "QML plugins:\n"; - for (const QString &p : qAsConst(qmlScanResult.plugins)) - std::wcout << " " << QDir::toNativeSeparators(p) << '\n'; - } - } - } - } - - QString platformPlugin; - // Sort apart Qt 5 libraries in the ones that are represented by the - // QtModule enumeration (and thus controlled by flags) and others. - QStringList deployedQtLibraries; - for (int i = 0 ; i < dependentQtLibs.size(); ++i) { - if (const quint64 qtm = qtModule(dependentQtLibs.at(i), infix)) - result.usedQtLibraries |= qtm; - else - deployedQtLibraries.push_back(dependentQtLibs.at(i)); // Not represented by flag. - } - result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries; - - const QStringList plugins = findQtPlugins( - &result.deployedQtLibraries, - // For non-QML applications, disable QML to prevent it from being pulled in by the - // qtaccessiblequick plugin. - options.disabledLibraries | (usesQml2 ? 0 : (QtQmlModule | QtQuickModule)), - options.disabledPlugins, qtpathsVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")), - libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin); - - // Apply options flags and re-add library names. - QString qtGuiLibrary; - for (const auto &qtModule : qtModuleEntries) { - if (result.deployedQtLibraries & qtModule.module) { - const QString library = libraryPath(libraryLocation, qtModule.libraryName, qtLibInfix, options.platform, result.isDebug); - deployedQtLibraries.append(library); - if (qtModule.module == QtGuiModule) - qtGuiLibrary = library; - } - } - - if (optVerboseLevel >= 1) { - std::wcout << "Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData() - << "\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData() - << "\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() << '\n'; - } - - if (optVerboseLevel > 1) - std::wcout << "Plugins: " << plugins.join(QLatin1Char(',')) << '\n'; - - if ((result.deployedQtLibraries & QtGuiModule) && platformPlugin.isEmpty()) { - *errorMessage =QStringLiteral("Unable to find the platform plugin."); - return result; - } - - if (options.platform.testFlag(WindowsBased) && !qtGuiLibrary.isEmpty()) { - const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, options.platform, errorMessage); - const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral("opengl32"), Qt::CaseInsensitive).isEmpty(); - if (options.softwareRasterizer && !dependsOnOpenGl) { - const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral("opengl32sw") + QLatin1String(windowsSharedLibrarySuffix)); - if (softwareRasterizer.isFile()) - deployedQtLibraries.append(softwareRasterizer.absoluteFilePath()); - } - if (options.systemD3dCompiler && machineArch != IMAGE_FILE_MACHINE_ARM64) { - const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir, wordSize); - if (d3dCompiler.isEmpty()) { - std::wcerr << "Warning: Cannot find any version of the d3dcompiler DLL.\n"; - } else { - deployedQtLibraries.push_back(d3dCompiler); - } - } - } // Windows - - // Update libraries - if (options.libraries) { - const QString targetPath = options.libraryDirectory.isEmpty() ? - options.directory : options.libraryDirectory; - QStringList libraries = deployedQtLibraries; - if (options.compilerRunTime) - libraries.append(compilerRunTimeLibs(options.platform, result.isDebug, machineArch)); - for (const QString &qtLib : qAsConst(libraries)) { - if (!updateLibrary(qtLib, targetPath, options, errorMessage)) - return result; - } - - if (options.patchQt && !options.dryRun) { - const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation, "Qt6Core", qtLibInfix, - options.platform, result.isDebug)).fileName(); -#ifndef QT_RELOCATABLE - if (!patchQtCore(targetPath + QLatin1Char('/') + qt6CoreName, errorMessage)) { - std::wcerr << "Warning: " << *errorMessage << '\n'; - errorMessage->clear(); - } -#endif - } - } // optLibraries - - // Update plugins - if (options.plugins) { - const QString targetPath = options.pluginDirectory.isEmpty() ? - options.directory : options.pluginDirectory; - QDir dir(targetPath); - if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) { - *errorMessage = QLatin1String("Cannot create ") + - QDir::toNativeSeparators(dir.absolutePath()) + QLatin1Char('.'); - return result; - } - for (const QString &plugin : plugins) { - const QString targetDirName = plugin.section(slash, -2, -2); - const QString targetPath = dir.absoluteFilePath(targetDirName); - if (!dir.exists(targetDirName)) { - if (optVerboseLevel) - std::wcout << "Creating directory " << targetPath << ".\n"; - if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) { - *errorMessage = QStringLiteral("Cannot create ") + targetDirName + QLatin1Char('.'); - return result; - } - } - if (!updateLibrary(plugin, targetPath, options, errorMessage)) - return result; - } - } // optPlugins - - // Update Quick imports - const bool usesQuick1 = result.deployedQtLibraries & QtDeclarativeModule; - // Do not be fooled by QtWebKit.dll depending on Quick into always installing Quick imports - // for WebKit1-applications. Check direct dependency only. - if (options.quickImports && (usesQuick1 || usesQml2)) { - if (usesQml2) { - for (const QmlImportScanResult::Module &module : qAsConst(qmlScanResult.modules)) { - const QString installPath = module.installPath(options.directory); - if (optVerboseLevel > 1) - std::wcout << "Installing: '" << module.name - << "' from " << module.sourcePath << " to " - << QDir::toNativeSeparators(installPath) << '\n'; - if (installPath != options.directory && !createDirectory(installPath, errorMessage)) - return result; - unsigned updateFileFlags = options.updateFileFlags | SkipQmlDesignerSpecificsDirectories; - unsigned qmlDirectoryFileFlags = 0; - if (options.deployPdb) - qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb; - if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(options.platform, debugMatchMode, qmlDirectoryFileFlags), - installPath, updateFileFlags, options.json, errorMessage)) { - return result; - } - } - } // Quick 2 - if (usesQuick1) { - const QString quick1ImportPath = - qtpathsVariables.value(QStringLiteral("QT_INSTALL_IMPORTS")); - const QmlDirectoryFileEntryFunction qmlFileEntryFunction(options.platform, debugMatchMode, options.deployPdb ? QmlDirectoryFileEntryFunction::DeployPdb : 0); - QStringList quick1Imports(QStringLiteral("Qt")); - for (const QString &quick1Import : qAsConst(quick1Imports)) { - const QString sourceFile = quick1ImportPath + slash + quick1Import; - if (!updateFile(sourceFile, qmlFileEntryFunction, options.directory, options.updateFileFlags, options.json, errorMessage)) - return result; - } - } // Quick 1 - } // optQuickImports - - if (options.translations) { - if (!options.dryRun && !createDirectory(options.translationsDirectory, errorMessage)) - return result; - if (!deployTranslations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")), - result.deployedQtLibraries, options.translationsDirectory, options, - errorMessage)) { - return result; - } - } - - result.success = true; - return result; -} - -static bool deployWebProcess(const QMap<QString, QString> &qtpathsVariables, const char *binaryName, - const Options &sourceOptions, QString *errorMessage) -{ - // Copy the web process and its dependencies - const QString webProcess = webProcessBinary(binaryName, sourceOptions.platform); - const QString webProcessSource = qtpathsVariables.value(QStringLiteral("QT_INSTALL_LIBEXECS")) - + QLatin1Char('/') + webProcess; - if (!updateFile(webProcessSource, sourceOptions.directory, sourceOptions.updateFileFlags, sourceOptions.json, errorMessage)) - return false; - Options options(sourceOptions); - options.binaries.append(options.directory + QLatin1Char('/') + webProcess); - options.quickImports = false; - options.translations = false; - return deploy(options, qtpathsVariables, errorMessage); -} - -static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables, - const Options &options, bool isDebug, QString *errorMessage) -{ - static const char *installDataFiles[] = {"icudtl.dat", - "qtwebengine_devtools_resources.pak", - "qtwebengine_resources.pak", - "qtwebengine_resources_100p.pak", - "qtwebengine_resources_200p.pak"}; - QByteArray webEngineProcessName(webEngineProcessC); - if (isDebug && platformHasDebugSuffix(options.platform)) - webEngineProcessName.append('d'); - if (optVerboseLevel) - std::wcout << "Deploying: " << webEngineProcessName.constData() << "...\n"; - if (!deployWebProcess(qtpathsVariables, webEngineProcessName, options, errorMessage)) - return false; - const QString resourcesSubDir = QStringLiteral("/resources"); - const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_DATA")) - + resourcesSubDir + QLatin1Char('/'); - const QString resourcesTargetDir(options.directory + resourcesSubDir); - if (!createDirectory(resourcesTargetDir, errorMessage)) - return false; - for (auto installDataFile : installDataFiles) { - if (!updateFile(resourcesSourceDir + QLatin1String(installDataFile), - resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) { - return false; - } - } - const QFileInfo translations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")) - + QStringLiteral("/qtwebengine_locales")); - if (!translations.isDir()) { - std::wcerr << "Warning: Cannot find the translation files of the QtWebEngine module at " - << QDir::toNativeSeparators(translations.absoluteFilePath()) << ".\n"; - return true; - } - if (options.translations) { - // Copy the whole translations directory. - return createDirectory(options.translationsDirectory, errorMessage) - && updateFile(translations.absoluteFilePath(), options.translationsDirectory, - options.updateFileFlags, options.json, errorMessage); - } - // Translations have been turned off, but QtWebEngine needs at least one. - const QFileInfo enUSpak(translations.filePath() + QStringLiteral("/en-US.pak")); - if (!enUSpak.exists()) { - std::wcerr << "Warning: Cannot find " - << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) << ".\n"; - return true; - } - const QString webEngineTranslationsDir = options.translationsDirectory + QLatin1Char('/') - + translations.fileName(); - if (!createDirectory(webEngineTranslationsDir, errorMessage)) - return false; - return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir, - options.updateFileFlags, options.json, errorMessage); -} - -QT_END_NAMESPACE - -QT_USE_NAMESPACE - -int main(int argc, char **argv) -{ - QCoreApplication a(argc, argv); - QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); - - const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath())); - QByteArray path = qgetenv("PATH"); - if (!path.contains(qtBinPath)) { // QTBUG-39177, ensure Qt is in the path so that qt.conf is taken into account. - path += ';'; - path += qtBinPath; - qputenv("PATH", path); - } - - Options options; - QString errorMessage; - - { // Command line - QCommandLineParser parser; - QString errorMessage; - const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage); - if (result & CommandLineParseError) - std::wcerr << errorMessage << "\n\n"; - if (result & CommandLineParseHelpRequested) - std::fputs(qPrintable(helpText(parser)), stdout); - if (result & CommandLineParseError) - return 1; - if (result & CommandLineParseHelpRequested) - return 0; - } - - const QMap<QString, QString> qtpathsVariables = - queryQtPaths(options.qtpathsBinary, &errorMessage); - const QString xSpec = qtpathsVariables.value(QStringLiteral("QMAKE_XSPEC")); - options.platform = platformFromMkSpec(xSpec); - - if (qtpathsVariables.isEmpty() || xSpec.isEmpty() - || !qtpathsVariables.contains(QStringLiteral("QT_INSTALL_BINS"))) { - std::wcerr << "Unable to query qtpaths: " << errorMessage << '\n'; - return 1; - } - - if (options.platform == UnknownPlatform) { - std::wcerr << "Unsupported platform " << xSpec << '\n'; - return 1; - } - - // Create directories - if (!createDirectory(options.directory, &errorMessage)) { - std::wcerr << errorMessage << '\n'; - return 1; - } - if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory - && !createDirectory(options.libraryDirectory, &errorMessage)) { - std::wcerr << errorMessage << '\n'; - return 1; - } - - const DeployResult result = deploy(options, qtpathsVariables, &errorMessage); - if (!result) { - std::wcerr << errorMessage << '\n'; - return 1; - } - - if (result.deployedQtLibraries & QtWebEngineCoreModule) { - if (!deployWebEngineCore(qtpathsVariables, options, result.isDebug, &errorMessage)) { - std::wcerr << errorMessage << '\n'; - return 1; - } - } - - if (options.json) { - if (options.list) - std::fputs(options.json->toList(options.list, options.directory).constData(), stdout); - else - std::fputs(options.json->toJson().constData(), stdout); - delete options.json; - options.json = nullptr; - } - - return 0; -} diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 10a33b85c..d800c6fe4 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -21,9 +21,3 @@ endif() # add_subdirectory(cmake/linguist) # endif() # special case end -if(QT_FEATURE_process AND WIN32 AND NOT CMAKE_CROSSCOMPILING) - add_subdirectory(windeployqt) -endif() -if(MACOS AND QT_FEATURE_process) - add_subdirectory(macdeployqt) -endif() diff --git a/tests/auto/macdeployqt/CMakeLists.txt b/tests/auto/macdeployqt/CMakeLists.txt deleted file mode 100644 index 073c2a9e7..000000000 --- a/tests/auto/macdeployqt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Generated from macdeployqt.pro. - -##################################################################### -## tst_macdeployqt Test: -##################################################################### - -qt_internal_add_test(tst_macdeployqt - SOURCES - tst_macdeployqt.cpp -) diff --git a/tests/auto/macdeployqt/source_basicapp/basicapp.pro b/tests/auto/macdeployqt/source_basicapp/basicapp.pro deleted file mode 100644 index bba41b9c1..000000000 --- a/tests/auto/macdeployqt/source_basicapp/basicapp.pro +++ /dev/null @@ -1 +0,0 @@ -SOURCES = main.cpp diff --git a/tests/auto/macdeployqt/source_basicapp/main.cpp b/tests/auto/macdeployqt/source_basicapp/main.cpp deleted file mode 100644 index 093a882f3..000000000 --- a/tests/auto/macdeployqt/source_basicapp/main.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QGuiApplication> -#include <QRasterWindow> -#include <QScreen> -#include <QTimer> - -// Simple test application just to verify that it comes up properly - -int main(int argc, char ** argv) -{ - QGuiApplication app(argc, argv); - QRasterWindow w; - w.setTitle("macdeployqt test application"); - w.show(); - QTimer::singleShot(200, &w, &QCoreApplication::quit); - return app.exec(); -} diff --git a/tests/auto/macdeployqt/source_plugin_sqlite/main.cpp b/tests/auto/macdeployqt/source_plugin_sqlite/main.cpp deleted file mode 100644 index 31e2e8117..000000000 --- a/tests/auto/macdeployqt/source_plugin_sqlite/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QtSql> - -int main(int argc, char ** argv) -{ - QCoreApplication app(argc, argv); - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); - return db.isValid() ? 0 : 1; -} diff --git a/tests/auto/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro b/tests/auto/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro deleted file mode 100644 index e8183d3ce..000000000 --- a/tests/auto/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES = main.cpp -QT += sql diff --git a/tests/auto/macdeployqt/source_plugin_tls/main.cpp b/tests/auto/macdeployqt/source_plugin_tls/main.cpp deleted file mode 100644 index 7d1070b2c..000000000 --- a/tests/auto/macdeployqt/source_plugin_tls/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QtNetwork> - -int main(int argc, char ** argv) -{ - QCoreApplication app(argc, argv); - return QSslSocket::supportsSsl() ? 0 : 1; -} diff --git a/tests/auto/macdeployqt/source_plugin_tls/plugin_tls.pro b/tests/auto/macdeployqt/source_plugin_tls/plugin_tls.pro deleted file mode 100644 index 23954f594..000000000 --- a/tests/auto/macdeployqt/source_plugin_tls/plugin_tls.pro +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES = main.cpp -QT += network diff --git a/tests/auto/macdeployqt/tst_macdeployqt.cpp b/tests/auto/macdeployqt/tst_macdeployqt.cpp deleted file mode 100644 index 892202557..000000000 --- a/tests/auto/macdeployqt/tst_macdeployqt.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QtCore> -#include <QtTest> - -bool g_testDirectoryBuild = false; // toggle to keep build output for debugging. -QTemporaryDir *g_temporaryDirectory; -QString g_macdeployqtBinary; -QString g_qmakeBinary; -QString g_makeBinary; -QString g_installNameToolBinary; - -#if QT_CONFIG(process) - -static const QString msgProcessError(const QProcess &process, const QString &what) -{ - QString result; - QTextStream(&result) << what << ": \"" << process.program() << ' ' - << process.arguments().join(QLatin1Char(' ')) << "\": " << process.errorString(); - return result; -} - -static bool runProcess(const QString &binary, - const QStringList &arguments, - QString *errorMessage, - const QString &workingDir = QString(), - const QProcessEnvironment &env = QProcessEnvironment(), - int timeOut = 10000, - QByteArray *stdOut = nullptr, QByteArray *stdErr = nullptr) -{ - QProcess process; - if (!env.isEmpty()) - process.setProcessEnvironment(env); - if (!workingDir.isEmpty()) - process.setWorkingDirectory(workingDir); - process.start(binary, arguments, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - *errorMessage = msgProcessError(process, "Failed to start"); - return false; - } - if (!process.waitForFinished(timeOut)) { - *errorMessage = msgProcessError(process, "Timed out"); - process.terminate(); - if (!process.waitForFinished(300)) - process.kill(); - return false; - } - if (stdOut) - *stdOut = process.readAllStandardOutput(); - if (stdErr) - *stdErr= process.readAllStandardError(); - if (process.exitStatus() != QProcess::NormalExit) { - *errorMessage = msgProcessError(process, "Crashed"); - return false; - } - if (process.exitCode() != QProcess::NormalExit) { - *errorMessage = msgProcessError(process, "Exit code " + QString::number(process.exitCode())); - return false; - } - return true; -} - -#else - -static bool runProcess(const QString &binary, - const QStringList &arguments, - QString *arguments, - const QString &workingDir = QString(), - const QProcessEnvironment &env = QProcessEnvironment(), - int timeOut = 5000, - QByteArray *stdOut = Q_NULLPTR, QByteArray *stdErr = Q_NULLPTR) -{ - Q_UNUSED(binary); - Q_UNUSED(arguments); - Q_UNUSED(arguments); - Q_UNUSED(workingDir); - Q_UNUSED(env); - Q_UNUSED(timeOut); - Q_UNUSED(stdOut); - Q_UNUSED(stdErr); - return false; -} - -#endif - -QString sourcePath(const QString &name) -{ - return "source_" + name; -} - -QString buildPath(const QString &name) -{ - if (g_testDirectoryBuild) - return "build_" + name; - return g_temporaryDirectory->path() + "/build_" + name; -} - -bool qmake(const QString &source, const QString &destination, QString *errorMessage) -{ - QStringList args = QStringList() << source; - return runProcess(g_qmakeBinary, args, errorMessage, destination); -} - -bool make(const QString &destination, QString *errorMessage) -{ - QStringList args; - return runProcess(g_makeBinary, args, errorMessage, destination, - {}, 60000); -} - -void build(const QString &name) -{ - // Build the app or framework according to the convention used - // by this test: - // source_name (source code) - // build_name (build artifacts) - - QString source = sourcePath(name); - QString build = buildPath(name); - QString profile = name + ".pro"; - - QString sourcePath = QFINDTESTDATA(source); - QVERIFY(!sourcePath.isEmpty()); - - // Clear/set up build dir - QString buildPath = build; - QVERIFY(QDir(buildPath).removeRecursively()); - QVERIFY(QDir().mkdir(buildPath)); - QVERIFY(QDir(buildPath).exists()); - - // Build application - QString sourceProFile = QDir(sourcePath).canonicalPath() + '/' + profile; - QString errorMessage; - QVERIFY2(qmake(sourceProFile, buildPath, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(make(buildPath, &errorMessage), qPrintable(errorMessage)); -} - -bool changeInstallName(const QString &path, const QString &binary, const QString &from, const QString &to) -{ - QStringList args = QStringList() << binary << "-change" << from << to; - QString errorMessage; - return runProcess(g_installNameToolBinary, args, &errorMessage, path); -} - -bool deploy(const QString &name, const QStringList &options, QString *errorMessage) -{ - QString bundle = name + ".app"; - QString path = buildPath(name); - QStringList args = QStringList() << bundle << options; - return runProcess(g_macdeployqtBinary, args, errorMessage, path); -} - -bool debugDeploy(const QString &name, const QStringList &options, QString *errorMessage) -{ - QString bundle = name + ".app"; - QString path = buildPath(name); - QStringList args = QStringList() << bundle << options << "-verbose=3"; - QByteArray stdOut; - QByteArray stdErr; - bool exitOK = runProcess(g_macdeployqtBinary, args, errorMessage, path, QProcessEnvironment(), - 10000, &stdOut, &stdErr); - - qDebug() << "macdeployqt exit OK" << exitOK; - qDebug() << qPrintable(stdOut); - qDebug() << qPrintable(stdErr); - - return exitOK; -} - -bool run(const QString &name, QString *errorMessage) -{ - QString path = buildPath(name); - QStringList args; - QString binary = name + ".app/Contents/MacOS/" + name; - return runProcess(binary, args, errorMessage, path); -} - -bool runPrintLibraries(const QString &name, QString *errorMessage, QByteArray *stdErr) -{ - QString binary = name + ".app/Contents/MacOS/" + name; - QString path = buildPath(name); - QStringList args; - QProcessEnvironment env = QProcessEnvironment(); - env.insert("DYLD_PRINT_LIBRARIES", "true"); - QByteArray stdOut; - return runProcess(binary, args, errorMessage, path, env, 5000, &stdOut, stdErr); -} - -void runVerifyDeployment(const QString &name) -{ - QString errorMessage; - // Verify that the application runs after deployment and that it loads binaries from - // the application bundle only. - QByteArray libraries; - QVERIFY2(runPrintLibraries(name, &errorMessage, &libraries), qPrintable(errorMessage)); - const QList<QString> parts = QString::fromLocal8Bit(libraries).split("dyld: loaded:"); - const QString qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath); - // Let assume Qt is not installed in system - foreach (QString part, parts) { - part = part.trimmed(); - if (part.isEmpty()) - continue; - QVERIFY(!parts.startsWith(qtPath)); - } -} - -class tst_macdeployqt : public QObject -{ - Q_OBJECT -private slots: - void initTestCase(); - void cleanupTestCase(); - void basicapp(); - void plugins_data(); - void plugins(); -}; - -void tst_macdeployqt::initTestCase() -{ -#ifdef QT_NO_PROCESS - QSKIP("This test requires QProcess support"); -#endif - - // Set up test-global unique temporary directory - g_temporaryDirectory = new QTemporaryDir(); - QVERIFY(g_temporaryDirectory->isValid()); - - // Locate build and deployment tools - g_macdeployqtBinary = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/macdeployqt"; - QVERIFY(!g_macdeployqtBinary.isEmpty()); - g_qmakeBinary = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmake"; - QVERIFY(!g_qmakeBinary.isEmpty()); - g_makeBinary = QStandardPaths::findExecutable("make"); - QVERIFY(!g_makeBinary.isEmpty()); - g_installNameToolBinary = QStandardPaths::findExecutable("install_name_tool"); - QVERIFY(!g_installNameToolBinary.isEmpty()); -} - -void tst_macdeployqt::cleanupTestCase() -{ - delete g_temporaryDirectory; -} - -// Verify that deployment of a basic Qt Gui application works -void tst_macdeployqt::basicapp() -{ -#ifdef QT_NO_PROCESS - QSKIP("This test requires QProcess support"); -#endif - - QString errorMessage; - QString name = "basicapp"; - - // Build and verify that the application runs before deployment - build(name); - QVERIFY2(run(name, &errorMessage), qPrintable(errorMessage)); - - // Deploy application - QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage)); - - // Verify deployment - runVerifyDeployment(name); -} - -void tst_macdeployqt::plugins_data() -{ - QTest::addColumn<QString>("name"); - QTest::newRow("sqlite") << "plugin_sqlite"; - QTest::newRow("tls") << "plugin_tls"; -} - -void tst_macdeployqt::plugins() -{ - QFETCH(QString, name); - - build(name); - - // Verify that the test app runs before deployment. - QString errorMessage; - if (!run(name, &errorMessage)) { - qDebug() << qPrintable(errorMessage); - QSKIP("Could not run test application before deployment"); - } - - QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage)); - runVerifyDeployment(name); -} - -QTEST_MAIN(tst_macdeployqt) -#include "tst_macdeployqt.moc" diff --git a/tests/auto/windeployqt/CMakeLists.txt b/tests/auto/windeployqt/CMakeLists.txt deleted file mode 100644 index 5eae1ca16..000000000 --- a/tests/auto/windeployqt/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Generated from windeployqt.pro. - -add_subdirectory(testapp) -add_subdirectory(test) diff --git a/tests/auto/windeployqt/test/CMakeLists.txt b/tests/auto/windeployqt/test/CMakeLists.txt deleted file mode 100644 index d412249a7..000000000 --- a/tests/auto/windeployqt/test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Generated from test.pro. - -##################################################################### -## tst_windeployqt Test: -##################################################################### - -qt_internal_add_test(tst_windeployqt - OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" # special case - SOURCES - ../tst_windeployqt.cpp -) diff --git a/tests/auto/windeployqt/testapp/CMakeLists.txt b/tests/auto/windeployqt/testapp/CMakeLists.txt deleted file mode 100644 index 83851dae6..000000000 --- a/tests/auto/windeployqt/testapp/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Generated from testapp.pro. - -##################################################################### -## testapp Binary: -##################################################################### - -qt_internal_add_executable(windeploy_testapp # special case - GUI - OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" - SOURCES - main.cpp - PUBLIC_LIBRARIES - Qt::Gui -) - -# special case begin -set_target_properties(windeploy_testapp - PROPERTIES - OUTPUT_NAME testapp -) -# special case end diff --git a/tests/auto/windeployqt/testapp/main.cpp b/tests/auto/windeployqt/testapp/main.cpp deleted file mode 100644 index 4dc03cdad..000000000 --- a/tests/auto/windeployqt/testapp/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QGuiApplication> -#include <QRasterWindow> -#include <QScreen> -#include <QTimer> - -// Simple test application just to verify that it comes up properly - -int main(int argc, char ** argv) -{ - QGuiApplication app(argc, argv); - QRasterWindow w; - w.setTitle("windeployqt test application"); - const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); - w.resize(availableGeometry.size() / 4); - w.show(); - QTimer::singleShot(200, &w, &QCoreApplication::quit); - return app.exec(); -} diff --git a/tests/auto/windeployqt/tst_windeployqt.cpp b/tests/auto/windeployqt/tst_windeployqt.cpp deleted file mode 100644 index 38403c90f..000000000 --- a/tests/auto/windeployqt/tst_windeployqt.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore/QLibraryInfo> -#include <QtCore/QProcess> -#include <QtCore/QProcessEnvironment> -#include <QtCore/QStandardPaths> -#include <QtCore/QTextStream> -#include <QtTest/QtTest> - -static const QString msgProcessError(const QProcess &process, const QString &what, - const QByteArray &stdOut = QByteArray(), - const QByteArray &stdErr = QByteArray()) -{ - QString result; - QTextStream str(&result); - str << what << ": \"" << process.program() << ' ' - << process.arguments().join(QLatin1Char(' ')) << "\": " << process.errorString(); - if (!stdOut.isEmpty()) - str << "\nStandard output:\n" << stdOut; - if (!stdErr.isEmpty()) - str << "\nStandard error:\n" << stdErr; - return result; -} - -static bool runProcess(const QString &binary, - const QStringList &arguments, - QString *errorMessage, - const QString &workingDir = QString(), - const QProcessEnvironment &env = QProcessEnvironment(), - int timeOut = 5000, - QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr) -{ - QProcess process; - if (!env.isEmpty()) - process.setProcessEnvironment(env); - if (!workingDir.isEmpty()) - process.setWorkingDirectory(workingDir); - qDebug().noquote().nospace() << "Running: " << QDir::toNativeSeparators(binary) - << ' ' << arguments.join(QLatin1Char(' ')); - process.start(binary, arguments, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - *errorMessage = msgProcessError(process, "Failed to start"); - return false; - } - if (!process.waitForFinished(timeOut)) { - *errorMessage = msgProcessError(process, "Timed out"); - process.terminate(); - if (!process.waitForFinished(300)) - process.kill(); - return false; - } - const QByteArray stdOut = process.readAllStandardOutput(); - const QByteArray stdErr = process.readAllStandardError(); - if (stdOutIn) - *stdOutIn = stdOut; - if (stdErrIn) - *stdErrIn = stdErr; - if (process.exitStatus() != QProcess::NormalExit) { - *errorMessage = msgProcessError(process, "Crashed", stdOut, stdErr); - return false; - } - if (process.exitCode() != QProcess::NormalExit) { - *errorMessage = msgProcessError(process, "Exit code " + QString::number(process.exitCode()), - stdOut, stdErr); - return false; - } - return true; -} - -class tst_windeployqt : public QObject -{ - Q_OBJECT -private slots: - void initTestCase(); - void help(); - void deploy(); - -private: - QString m_windeployqtBinary; - QString m_testApp; - QString m_testAppBinary; -}; - -void tst_windeployqt::initTestCase() -{ - m_windeployqtBinary = QStandardPaths::findExecutable("windeployqt"); - QVERIFY(!m_windeployqtBinary.isEmpty()); - m_testApp = QFINDTESTDATA("testapp"); - QVERIFY(!m_testApp.isEmpty()); - const QFileInfo testAppBinary(m_testApp + QLatin1String("/testapp.exe")); - QVERIFY2(testAppBinary.isFile(), qPrintable(testAppBinary.absoluteFilePath())); - m_testAppBinary = testAppBinary.absoluteFilePath(); -} - -void tst_windeployqt::help() -{ - QString errorMessage; - QByteArray stdOut; - QByteArray stdErr; - QVERIFY2(runProcess(m_windeployqtBinary, QStringList("--help"), &errorMessage, - QString(), QProcessEnvironment(), 5000, &stdOut, &stdErr), - qPrintable(errorMessage)); - QVERIFY2(!stdOut.isEmpty(), stdErr); -} - -// deploy(): Deploys the test application and launches it with Qt removed from the environment -// to verify it runs stand-alone. - -void tst_windeployqt::deploy() -{ - QString errorMessage; - // Deploy application - QStringList deployArguments; - deployArguments << QLatin1String("--no-translations") << QDir::toNativeSeparators(m_testAppBinary); - QVERIFY2(runProcess(m_windeployqtBinary, deployArguments, &errorMessage, QString(), QProcessEnvironment(), 20000), - qPrintable(errorMessage)); - - // Create environment with Qt and all "lib" paths removed. - const QString qtBinDir = QDir::toNativeSeparators(QLibraryInfo::path(QLibraryInfo::BinariesPath)); - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - const QString pathKey = QLatin1String("PATH"); - const QChar pathSeparator(QLatin1Char(';')); // ### fixme: Qt 5.6: QDir::listSeparator() - const QString origPath = env.value(pathKey); - QString newPath; - const QStringList pathElements = origPath.split(pathSeparator, Qt::SkipEmptyParts); - for (const QString &pathElement : pathElements) { - if (pathElement.compare(qtBinDir, Qt::CaseInsensitive) - && !pathElement.contains(QLatin1String("\\lib"), Qt::CaseInsensitive)) { - if (!newPath.isEmpty()) - newPath.append(pathSeparator); - newPath.append(pathElement); - } - } - if (newPath == origPath) - qWarning() << "Unable to remove Qt from PATH"; - env.insert(pathKey, newPath); - - // Create qt.conf to enforce usage of local plugins - QFile qtConf(QFileInfo(m_testAppBinary).absolutePath() + QLatin1String("/qt.conf")); - QVERIFY2(qtConf.open(QIODevice::WriteOnly | QIODevice::Text), - qPrintable(qtConf.fileName() + QLatin1String(": ") + qtConf.errorString())); - QVERIFY(qtConf.write("[Paths]\nPrefix = .\n")); - qtConf.close(); - - // Verify that application still runs - QVERIFY2(runProcess(m_testAppBinary, QStringList(), &errorMessage, QString(), env, 10000), - qPrintable(errorMessage)); -} - -QTEST_MAIN(tst_windeployqt) -#include "tst_windeployqt.moc" |