From a2c9cb74959e3eb0bad4f085ac1b223826515330 Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Thu, 25 May 2017 13:18:21 +0200 Subject: [PATCH 1/5] change project structure to qt module --- .qmake.conf | 5 +++++ linuxdeployqt.pro | 3 +-- linuxdeployqt/linuxdeployqt.pro | 2 -- src/src.pro | 1 + sync.profile | 5 +++++ tests/tests.pro | 11 +++++++++++ tools/linuxdeployqt/linuxdeployqt.pro | 17 +++++++++++++++++ {linuxdeployqt => tools/linuxdeployqt}/main.cpp | 0 {shared => tools/shared}/shared.cpp | 0 {shared => tools/shared}/shared.h | 0 tools/tools.pro | 4 ++++ 11 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 .qmake.conf delete mode 100644 linuxdeployqt/linuxdeployqt.pro create mode 100644 src/src.pro create mode 100644 sync.profile create mode 100644 tests/tests.pro create mode 100644 tools/linuxdeployqt/linuxdeployqt.pro rename {linuxdeployqt => tools/linuxdeployqt}/main.cpp (100%) rename {shared => tools/shared}/shared.cpp (100%) rename {shared => tools/shared}/shared.h (100%) create mode 100644 tools/tools.pro diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 00000000..ae52f0b8 --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,5 @@ +load(qt_build_config) + +CONFIG += warning_clean exceptions + +MODULE_VERSION = 0.5.0 diff --git a/linuxdeployqt.pro b/linuxdeployqt.pro index 1e2f2d5b..58c33f27 100644 --- a/linuxdeployqt.pro +++ b/linuxdeployqt.pro @@ -1,2 +1 @@ -TEMPLATE = subdirs -SUBDIRS = linuxdeployqt +load(qt_parts) diff --git a/linuxdeployqt/linuxdeployqt.pro b/linuxdeployqt/linuxdeployqt.pro deleted file mode 100644 index b9e628af..00000000 --- a/linuxdeployqt/linuxdeployqt.pro +++ /dev/null @@ -1,2 +0,0 @@ -QT = core -SOURCES += main.cpp ../shared/shared.cpp diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 00000000..86290fc8 --- /dev/null +++ b/src/src.pro @@ -0,0 +1 @@ +TEMPLATE = aux diff --git a/sync.profile b/sync.profile new file mode 100644 index 00000000..58dbecbf --- /dev/null +++ b/sync.profile @@ -0,0 +1,5 @@ +%modules = ( +); + +%moduleheaders = ( +); diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..089e5904 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + QtQuickControls2Application \ + QtWebEngineApplication \ + QtWidgetsApplication + +DISTFILES += \ + tests-ci.sh \ + tests-environment.sh \ + tests.sh diff --git a/tools/linuxdeployqt/linuxdeployqt.pro b/tools/linuxdeployqt/linuxdeployqt.pro new file mode 100644 index 00000000..5c73e74e --- /dev/null +++ b/tools/linuxdeployqt/linuxdeployqt.pro @@ -0,0 +1,17 @@ +option(host_build) + +QT = core +CONFIG += console + +TARGET = linuxdeployqt +VERSION = $$MODULE_VERSION + +DEFINES += BUILD_LINUXDEPLOYQT + +load(qt_tool) + +HEADERS += ../shared/shared.h +SOURCES += main.cpp \ + ../shared/shared.cpp + +DEFINES -= QT_USE_QSTRINGBUILDER diff --git a/linuxdeployqt/main.cpp b/tools/linuxdeployqt/main.cpp similarity index 100% rename from linuxdeployqt/main.cpp rename to tools/linuxdeployqt/main.cpp diff --git a/shared/shared.cpp b/tools/shared/shared.cpp similarity index 100% rename from shared/shared.cpp rename to tools/shared/shared.cpp diff --git a/shared/shared.h b/tools/shared/shared.h similarity index 100% rename from shared/shared.h rename to tools/shared/shared.h diff --git a/tools/tools.pro b/tools/tools.pro new file mode 100644 index 00000000..91ee9141 --- /dev/null +++ b/tools/tools.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS += linuxdeployqt From 60efbcaf029ae03afccf8233a50474326d0578e0 Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Thu, 25 May 2017 13:21:13 +0200 Subject: [PATCH 2/5] fixed (?) tests --- tests/tests-ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests-ci.sh b/tests/tests-ci.sh index 9bd5fa36..a04a105d 100755 --- a/tests/tests-ci.sh +++ b/tests/tests-ci.sh @@ -10,8 +10,8 @@ mkdir -p linuxdeployqt.AppDir/usr/bin/ cp /usr/local/bin/{appimagetool,mksquashfs,patchelf,zsyncmake} linuxdeployqt.AppDir/usr/bin/ find linuxdeployqt.AppDir/ export VERSION=continuous -cp ./linuxdeployqt/linuxdeployqt linuxdeployqt.AppDir/usr/bin/ -./linuxdeployqt/linuxdeployqt linuxdeployqt.AppDir/linuxdeployqt.desktop -verbose=3 -appimage +cp ./bin/linuxdeployqt linuxdeployqt.AppDir/usr/bin/ +./bin/linuxdeployqt linuxdeployqt.AppDir/linuxdeployqt.desktop -verbose=3 -appimage ls -lh find *.AppDir xpra start :99 From 71f0858aa8d54cece29ca6d68e97240bb216225b Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Mon, 19 Jun 2017 17:57:30 +0200 Subject: [PATCH 3/5] moved shared.h into main folder it was only shared for mac, becaused it was used by 2 tools --- tools/linuxdeployqt/linuxdeployqt.pro | 6 +- tools/linuxdeployqt/main.cpp | 768 ++++++++++----------- tools/{shared => linuxdeployqt}/shared.cpp | 0 tools/{shared => linuxdeployqt}/shared.h | 0 4 files changed, 387 insertions(+), 387 deletions(-) rename tools/{shared => linuxdeployqt}/shared.cpp (100%) rename tools/{shared => linuxdeployqt}/shared.h (100%) diff --git a/tools/linuxdeployqt/linuxdeployqt.pro b/tools/linuxdeployqt/linuxdeployqt.pro index 5c73e74e..26f115e0 100644 --- a/tools/linuxdeployqt/linuxdeployqt.pro +++ b/tools/linuxdeployqt/linuxdeployqt.pro @@ -10,8 +10,8 @@ DEFINES += BUILD_LINUXDEPLOYQT load(qt_tool) -HEADERS += ../shared/shared.h +HEADERS += shared.h SOURCES += main.cpp \ - ../shared/shared.cpp + shared.cpp -DEFINES -= QT_USE_QSTRINGBUILDER +DEFINES -= QT_USE_QSTRINGBUILDER #leads to compile errors if not disabled diff --git a/tools/linuxdeployqt/main.cpp b/tools/linuxdeployqt/main.cpp index 0bde0c22..bf760a79 100644 --- a/tools/linuxdeployqt/main.cpp +++ b/tools/linuxdeployqt/main.cpp @@ -28,7 +28,7 @@ #include #include #include -#include "../shared/shared.h" +#include "shared.h" #include #include #include @@ -36,387 +36,387 @@ int main(int argc, char **argv) { - QCoreApplication app(argc, argv); - - extern QString appBinaryPath; - appBinaryPath = ""; // Cannot do it in one go due to "extern" - - QString firstArgument = QString::fromLocal8Bit(argv[1]); - - if (argc < 2 || firstArgument.startsWith("-")) { - qDebug() << "Usage: linuxdeployqt [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() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs)"; - qDebug() << " -no-strip : Don't run 'strip' on the binaries"; - qDebug() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries"; - qDebug() << " -executable= : Let the given executable use the deployed libraries too"; - qDebug() << " -qmldir= : Scan for QML imports in the given path"; - qDebug() << " -always-overwrite : Copy files even if the target file exists"; - qDebug() << " -no-translations : Skip deployment of translations."; - qDebug() << ""; - qDebug() << "linuxdeployqt takes an application as input and makes it"; - qDebug() << "self-contained by copying in the Qt libraries and plugins that"; - qDebug() << "the application uses."; - qDebug() << ""; - qDebug() << "It deploys the Qt instance that qmake on the $PATH points to,"; - qDebug() << "so make sure that it is the correct one."; - qDebug() << ""; - qDebug() << "Plugins related to a Qt library are copied in with the library."; - /* TODO: To be implemented - qDebug() << "The accessibility, image formats, and text codec"; - qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; - */ - qDebug() << ""; - qDebug() << "See the \"Deploying Applications on Linux\" topic in the"; - qDebug() << "documentation for more information about deployment on Linux."; - - return 1; - } - - QString desktopFile = ""; - QString desktopExecEntry = ""; - QString desktopIconEntry = ""; - - if (argc > 1) { - /* If we got a desktop file as the argument, try to figure out the application binary from it. - * This has the advantage that we can also figure out the icon file this way, and have less work - * to do when using linuxdeployqt. */ - if (firstArgument.endsWith(".desktop")){ - qDebug() << "Desktop file as first argument:" << firstArgument; - QSettings * settings = 0; - settings = new QSettings(firstArgument, QSettings::IniFormat); - desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed(); - qDebug() << "desktopExecEntry:" << desktopExecEntry; - desktopFile = firstArgument; - desktopIconEntry = settings->value("Desktop Entry/Icon", "r").toString().split(' ').first().split('.').first().trimmed(); - qDebug() << "desktopIconEntry:" << desktopIconEntry; - - QString candidateBin = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + desktopExecEntry); // Not FHS-like - - /* Search directory for an executable with the name in the Exec= key */ - QString directoryToBeSearched; - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath()); - - QDirIterator it(directoryToBeSearched, QDirIterator::Subdirectories); - while (it.hasNext()) { - it.next(); - if((it.fileName() == desktopExecEntry) && (it.fileInfo().isFile()) && (it.fileInfo().isExecutable())){ - qDebug() << "Found binary from desktop file:" << it.fileInfo().canonicalFilePath(); - appBinaryPath = it.fileInfo().absoluteFilePath(); - break; - } - } - - /* Only if we could not find it below the directory in which the desktop file resides, search above */ - if(appBinaryPath == ""){ - if(QFileInfo(QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../bin/" + desktopExecEntry)).exists()){ - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../"); - } else { - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../"); - } - QDirIterator it2(directoryToBeSearched, QDirIterator::Subdirectories); - while (it2.hasNext()) { - it2.next(); - if((it2.fileName() == desktopExecEntry) && (it2.fileInfo().isFile()) && (it2.fileInfo().isExecutable())){ - qDebug() << "Found binary from desktop file:" << it2.fileInfo().canonicalFilePath(); - appBinaryPath = it2.fileInfo().absoluteFilePath(); - break; - } - } - } - - if(appBinaryPath == ""){ - if((QFileInfo(candidateBin).isFile()) && (QFileInfo(candidateBin).isExecutable())) { - appBinaryPath = QFileInfo(candidateBin).absoluteFilePath(); - } else { - LogError() << "Could not determine the path to the executable based on the desktop file\n"; - return 1; - } - } - - } else { - appBinaryPath = firstArgument; - appBinaryPath = QFileInfo(QDir::cleanPath(appBinaryPath)).absoluteFilePath(); - } - } - - // Allow binaries next to linuxdeployqt to be found; this is useful for bundling - // this application itself together with helper binaries such as patchelf - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString oldPath = env.value("PATH"); - QString newPath = QCoreApplication::applicationDirPath() + ":" + oldPath; - LogDebug() << newPath; - setenv("PATH",newPath.toUtf8().constData(),1); - - QString appName = QDir::cleanPath(QFileInfo(appBinaryPath).completeBaseName()); - - QString appDir = QDir::cleanPath(appBinaryPath + "/../"); - if (QDir().exists(appDir) == false) { - qDebug() << "Error: Could not find AppDir" << appDir; - return 1; - } - - bool plugins = true; - bool appimage = false; - extern bool runStripEnabled; - extern bool bundleAllButCoreLibs; - extern bool fhsLikeMode; - extern QString fhsPrefix; - extern bool alwaysOwerwriteEnabled; - extern QStringList librarySearchPath; - QStringList additionalExecutables; - bool qmldirArgumentUsed = false; - bool skipTranslations = false; - QStringList qmlDirs; - - /* FHS-like mode is for an application that has been installed to a $PREFIX which is otherwise empty, e.g., /path/to/usr. - * In this case, we want to construct an AppDir in /path/to. */ - if (QDir().exists((QDir::cleanPath(appBinaryPath + "/../../bin"))) == true) { - fhsPrefix = QDir::cleanPath(appBinaryPath + "/../../"); - qDebug() << "FHS-like mode with PREFIX, fhsPrefix:" << fhsPrefix; - fhsLikeMode = true; - } else { - qDebug() << "Not using FHS-like mode"; - } - - if (QDir().exists(appBinaryPath)) { - qDebug() << "app-binary:" << appBinaryPath; - } else { - qDebug() << "Error: Could not find app-binary" << appBinaryPath; - return 1; - } - - QString appDirPath; - QString relativeBinPath; - if(fhsLikeMode == false){ - appDirPath = appDir; - relativeBinPath = appName; - } else { - appDirPath = QDir::cleanPath(fhsPrefix + "/../"); - QString relativePrefix = fhsPrefix.replace(appDirPath+"/", ""); - relativeBinPath = relativePrefix + "/bin/" + appName; - } - qDebug() << "appDirPath:" << appDirPath; - qDebug() << "relativeBinPath:" << relativeBinPath; - - QFile appRun(appDirPath + "/AppRun"); - if(appRun.exists()){ - appRun.remove(); - } - - QFile::link(relativeBinPath, appDirPath + "/AppRun"); - - /* Copy the desktop file in place, into the top level of the AppDir */ - if(desktopFile != ""){ - QString destination = QDir::cleanPath(appDirPath + "/" + QFileInfo(desktopFile).fileName()); - if(QFileInfo(destination).exists() == false){ - if (QFile::copy(desktopFile, destination)){ - qDebug() << "Copied" << desktopFile << "to" << destination; - } - } - if(QFileInfo(destination).isFile() == false){ - LogError() << destination << "does not exist and could not be copied there\n"; - return 1; - } - } - - /* To make an AppDir, we need to find the icon and copy it in place */ - QStringList candidates; - QString iconToBeUsed = ""; - if(desktopIconEntry != ""){ - QDirIterator it3(appDirPath, QDirIterator::Subdirectories); - while (it3.hasNext()) { - it3.next(); - if((it3.fileName().startsWith(desktopIconEntry)) && ((it3.fileName().endsWith(".png")) || (it3.fileName().endsWith(".svg")) || (it3.fileName().endsWith(".svgz")) || (it3.fileName().endsWith(".xpm")))){ - candidates.append(it3.filePath()); - } - } - qDebug() << "Found icons from desktop file:" << candidates; - - /* Select the main icon from the candidates */ - if(candidates.length() == 1){ - iconToBeUsed = candidates.at(0); // The only choice - } else if(candidates.length() > 1){ - foreach(QString current, candidates) { - if(current.contains("256")){ - iconToBeUsed = current; - continue; - } - if(current.contains("128")){ - iconToBeUsed = current; - continue; - } - if(current.contains("svg")){ - iconToBeUsed = current; - continue; - } - if(current.contains("svgz")){ - iconToBeUsed = current; - continue; - } - if(current.contains("512")){ - iconToBeUsed = current; - continue; - } - if(current.contains("1024")){ - iconToBeUsed = current; - continue; - } - if(current.contains("64")){ - iconToBeUsed = current; - continue; - } - if(current.contains("48")){ - iconToBeUsed = current; - continue; - } - if(current.contains("xpm")){ - iconToBeUsed = current; - continue; - } - } - } - - /* Copy in place */ - if(iconToBeUsed != ""){ - /* Check if there is already an icon and only if there is not, copy it to the AppDir. - * As per the ROX AppDir spec, also copying to .DirIcon. */ - QString preExistingToplevelIcon = ""; - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".xpm").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".xpm"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svgz").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svgz"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".png").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".png"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - - if(preExistingToplevelIcon != ""){ - qDebug() << "preExistingToplevelIcon:" << preExistingToplevelIcon; - } else { - qDebug() << "iconToBeUsed:" << iconToBeUsed; - QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); - if (QFile::copy(iconToBeUsed, targetIconPath)){ - qDebug() << "Copied" << iconToBeUsed << "to" << targetIconPath; - QFile::copy(targetIconPath, appDirPath + "/.DirIcon"); - } else { - LogError() << "Could not copy" << iconToBeUsed << "to" << targetIconPath << "\n"; - exit(1); - } - } - } - } - - 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("-appimage")) { - LogDebug() << "Argument found:" << argument; - appimage = true; - bundleAllButCoreLibs = true; - } else if (argument == QByteArray("-no-strip")) { - LogDebug() << "Argument found:" << argument; - runStripEnabled = false; - } else if (argument == QByteArray("-bundle-non-qt-libs")) { - LogDebug() << "Argument found:" << argument; - bundleAllButCoreLibs = true; - } 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 == QByteArray("-always-overwrite")) { - LogDebug() << "Argument found:" << argument; - alwaysOwerwriteEnabled = true; - } else if (argument == QByteArray("-no-translations")) { - LogDebug() << "Argument found:" << argument; - skipTranslations = true; - } else if (argument.startsWith("-")) { - LogError() << "Unknown argument" << argument << "\n"; - return 1; - } - } - - if (appimage) { - if(checkAppImagePrerequisites(appDirPath) == false){ - LogError() << "checkAppImagePrerequisites failed\n"; - return 1; - } - } - - DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables); - - // Convenience: Look for .qml files in the current directoty if no -qmldir specified. - if (qmlDirs.isEmpty()) { - QDir dir; - if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) { - qmlDirs += QStringLiteral("."); - } - } - - if (!qmlDirs.isEmpty()) { - bool ok = deployQmlImports(appDirPath, deploymentInfo, qmlDirs); - if (!ok && qmldirArgumentUsed) - return 1; // exit if the user explicitly asked for qml import deployment - // Update deploymentInfo.deployedLibraries - the QML imports - // may have brought in extra libraries as dependencies. - deploymentInfo.deployedLibraries += findAppLibraries(appDirPath); - deploymentInfo.deployedLibraries = deploymentInfo.deployedLibraries.toSet().toList(); - } - - deploymentInfo.usedModulesMask = 0; - findUsedModules(deploymentInfo); - - if (plugins && !deploymentInfo.qtPath.isEmpty()) { - if (deploymentInfo.pluginPath.isEmpty()) - deploymentInfo.pluginPath = QDir::cleanPath(deploymentInfo.qtPath + "/../plugins"); - deployPlugins(appDirPath, deploymentInfo); - createQtConf(appDirPath); - } - - if (runStripEnabled) - stripAppBinary(appDirPath); - - if (!skipTranslations) { - deployTranslations(appDirPath, deploymentInfo.usedModulesMask); - } - - if (appimage) { - int result = createAppImage(appDirPath); - LogDebug() << "result:" << result; - exit(result); - } - exit(0); + QCoreApplication app(argc, argv); + + extern QString appBinaryPath; + appBinaryPath = ""; // Cannot do it in one go due to "extern" + + QString firstArgument = QString::fromLocal8Bit(argv[1]); + + if (argc < 2 || firstArgument.startsWith("-")) { + qDebug() << "Usage: linuxdeployqt [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() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs)"; + qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries"; + qDebug() << " -executable= : Let the given executable use the deployed libraries too"; + qDebug() << " -qmldir= : Scan for QML imports in the given path"; + qDebug() << " -always-overwrite : Copy files even if the target file exists"; + qDebug() << " -no-translations : Skip deployment of translations."; + qDebug() << ""; + qDebug() << "linuxdeployqt takes an application as input and makes it"; + qDebug() << "self-contained by copying in the Qt libraries and plugins that"; + qDebug() << "the application uses."; + qDebug() << ""; + qDebug() << "It deploys the Qt instance that qmake on the $PATH points to,"; + qDebug() << "so make sure that it is the correct one."; + qDebug() << ""; + qDebug() << "Plugins related to a Qt library are copied in with the library."; + /* TODO: To be implemented + qDebug() << "The accessibility, image formats, and text codec"; + qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; + */ + qDebug() << ""; + qDebug() << "See the \"Deploying Applications on Linux\" topic in the"; + qDebug() << "documentation for more information about deployment on Linux."; + + return 1; + } + + QString desktopFile = ""; + QString desktopExecEntry = ""; + QString desktopIconEntry = ""; + + if (argc > 1) { + /* If we got a desktop file as the argument, try to figure out the application binary from it. + * This has the advantage that we can also figure out the icon file this way, and have less work + * to do when using linuxdeployqt. */ + if (firstArgument.endsWith(".desktop")){ + qDebug() << "Desktop file as first argument:" << firstArgument; + QSettings * settings = 0; + settings = new QSettings(firstArgument, QSettings::IniFormat); + desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed(); + qDebug() << "desktopExecEntry:" << desktopExecEntry; + desktopFile = firstArgument; + desktopIconEntry = settings->value("Desktop Entry/Icon", "r").toString().split(' ').first().split('.').first().trimmed(); + qDebug() << "desktopIconEntry:" << desktopIconEntry; + + QString candidateBin = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + desktopExecEntry); // Not FHS-like + + /* Search directory for an executable with the name in the Exec= key */ + QString directoryToBeSearched; + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath()); + + QDirIterator it(directoryToBeSearched, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + if((it.fileName() == desktopExecEntry) && (it.fileInfo().isFile()) && (it.fileInfo().isExecutable())){ + qDebug() << "Found binary from desktop file:" << it.fileInfo().canonicalFilePath(); + appBinaryPath = it.fileInfo().absoluteFilePath(); + break; + } + } + + /* Only if we could not find it below the directory in which the desktop file resides, search above */ + if(appBinaryPath == ""){ + if(QFileInfo(QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../bin/" + desktopExecEntry)).exists()){ + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../"); + } else { + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../"); + } + QDirIterator it2(directoryToBeSearched, QDirIterator::Subdirectories); + while (it2.hasNext()) { + it2.next(); + if((it2.fileName() == desktopExecEntry) && (it2.fileInfo().isFile()) && (it2.fileInfo().isExecutable())){ + qDebug() << "Found binary from desktop file:" << it2.fileInfo().canonicalFilePath(); + appBinaryPath = it2.fileInfo().absoluteFilePath(); + break; + } + } + } + + if(appBinaryPath == ""){ + if((QFileInfo(candidateBin).isFile()) && (QFileInfo(candidateBin).isExecutable())) { + appBinaryPath = QFileInfo(candidateBin).absoluteFilePath(); + } else { + LogError() << "Could not determine the path to the executable based on the desktop file\n"; + return 1; + } + } + + } else { + appBinaryPath = firstArgument; + appBinaryPath = QFileInfo(QDir::cleanPath(appBinaryPath)).absoluteFilePath(); + } + } + + // Allow binaries next to linuxdeployqt to be found; this is useful for bundling + // this application itself together with helper binaries such as patchelf + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString oldPath = env.value("PATH"); + QString newPath = QCoreApplication::applicationDirPath() + ":" + oldPath; + LogDebug() << newPath; + setenv("PATH",newPath.toUtf8().constData(),1); + + QString appName = QDir::cleanPath(QFileInfo(appBinaryPath).completeBaseName()); + + QString appDir = QDir::cleanPath(appBinaryPath + "/../"); + if (QDir().exists(appDir) == false) { + qDebug() << "Error: Could not find AppDir" << appDir; + return 1; + } + + bool plugins = true; + bool appimage = false; + extern bool runStripEnabled; + extern bool bundleAllButCoreLibs; + extern bool fhsLikeMode; + extern QString fhsPrefix; + extern bool alwaysOwerwriteEnabled; + extern QStringList librarySearchPath; + QStringList additionalExecutables; + bool qmldirArgumentUsed = false; + bool skipTranslations = false; + QStringList qmlDirs; + + /* FHS-like mode is for an application that has been installed to a $PREFIX which is otherwise empty, e.g., /path/to/usr. + * In this case, we want to construct an AppDir in /path/to. */ + if (QDir().exists((QDir::cleanPath(appBinaryPath + "/../../bin"))) == true) { + fhsPrefix = QDir::cleanPath(appBinaryPath + "/../../"); + qDebug() << "FHS-like mode with PREFIX, fhsPrefix:" << fhsPrefix; + fhsLikeMode = true; + } else { + qDebug() << "Not using FHS-like mode"; + } + + if (QDir().exists(appBinaryPath)) { + qDebug() << "app-binary:" << appBinaryPath; + } else { + qDebug() << "Error: Could not find app-binary" << appBinaryPath; + return 1; + } + + QString appDirPath; + QString relativeBinPath; + if(fhsLikeMode == false){ + appDirPath = appDir; + relativeBinPath = appName; + } else { + appDirPath = QDir::cleanPath(fhsPrefix + "/../"); + QString relativePrefix = fhsPrefix.replace(appDirPath+"/", ""); + relativeBinPath = relativePrefix + "/bin/" + appName; + } + qDebug() << "appDirPath:" << appDirPath; + qDebug() << "relativeBinPath:" << relativeBinPath; + + QFile appRun(appDirPath + "/AppRun"); + if(appRun.exists()){ + appRun.remove(); + } + + QFile::link(relativeBinPath, appDirPath + "/AppRun"); + + /* Copy the desktop file in place, into the top level of the AppDir */ + if(desktopFile != ""){ + QString destination = QDir::cleanPath(appDirPath + "/" + QFileInfo(desktopFile).fileName()); + if(QFileInfo(destination).exists() == false){ + if (QFile::copy(desktopFile, destination)){ + qDebug() << "Copied" << desktopFile << "to" << destination; + } + } + if(QFileInfo(destination).isFile() == false){ + LogError() << destination << "does not exist and could not be copied there\n"; + return 1; + } + } + + /* To make an AppDir, we need to find the icon and copy it in place */ + QStringList candidates; + QString iconToBeUsed = ""; + if(desktopIconEntry != ""){ + QDirIterator it3(appDirPath, QDirIterator::Subdirectories); + while (it3.hasNext()) { + it3.next(); + if((it3.fileName().startsWith(desktopIconEntry)) && ((it3.fileName().endsWith(".png")) || (it3.fileName().endsWith(".svg")) || (it3.fileName().endsWith(".svgz")) || (it3.fileName().endsWith(".xpm")))){ + candidates.append(it3.filePath()); + } + } + qDebug() << "Found icons from desktop file:" << candidates; + + /* Select the main icon from the candidates */ + if(candidates.length() == 1){ + iconToBeUsed = candidates.at(0); // The only choice + } else if(candidates.length() > 1){ + foreach(QString current, candidates) { + if(current.contains("256")){ + iconToBeUsed = current; + continue; + } + if(current.contains("128")){ + iconToBeUsed = current; + continue; + } + if(current.contains("svg")){ + iconToBeUsed = current; + continue; + } + if(current.contains("svgz")){ + iconToBeUsed = current; + continue; + } + if(current.contains("512")){ + iconToBeUsed = current; + continue; + } + if(current.contains("1024")){ + iconToBeUsed = current; + continue; + } + if(current.contains("64")){ + iconToBeUsed = current; + continue; + } + if(current.contains("48")){ + iconToBeUsed = current; + continue; + } + if(current.contains("xpm")){ + iconToBeUsed = current; + continue; + } + } + } + + /* Copy in place */ + if(iconToBeUsed != ""){ + /* Check if there is already an icon and only if there is not, copy it to the AppDir. + * As per the ROX AppDir spec, also copying to .DirIcon. */ + QString preExistingToplevelIcon = ""; + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".xpm").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".xpm"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svgz").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svgz"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".png").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".png"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + + if(preExistingToplevelIcon != ""){ + qDebug() << "preExistingToplevelIcon:" << preExistingToplevelIcon; + } else { + qDebug() << "iconToBeUsed:" << iconToBeUsed; + QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); + if (QFile::copy(iconToBeUsed, targetIconPath)){ + qDebug() << "Copied" << iconToBeUsed << "to" << targetIconPath; + QFile::copy(targetIconPath, appDirPath + "/.DirIcon"); + } else { + LogError() << "Could not copy" << iconToBeUsed << "to" << targetIconPath << "\n"; + exit(1); + } + } + } + } + + 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("-appimage")) { + LogDebug() << "Argument found:" << argument; + appimage = true; + bundleAllButCoreLibs = true; + } else if (argument == QByteArray("-no-strip")) { + LogDebug() << "Argument found:" << argument; + runStripEnabled = false; + } else if (argument == QByteArray("-bundle-non-qt-libs")) { + LogDebug() << "Argument found:" << argument; + bundleAllButCoreLibs = true; + } 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 == QByteArray("-always-overwrite")) { + LogDebug() << "Argument found:" << argument; + alwaysOwerwriteEnabled = true; + } else if (argument == QByteArray("-no-translations")) { + LogDebug() << "Argument found:" << argument; + skipTranslations = true; + } else if (argument.startsWith("-")) { + LogError() << "Unknown argument" << argument << "\n"; + return 1; + } + } + + if (appimage) { + if(checkAppImagePrerequisites(appDirPath) == false){ + LogError() << "checkAppImagePrerequisites failed\n"; + return 1; + } + } + + DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables); + + // Convenience: Look for .qml files in the current directoty if no -qmldir specified. + if (qmlDirs.isEmpty()) { + QDir dir; + if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) { + qmlDirs += QStringLiteral("."); + } + } + + if (!qmlDirs.isEmpty()) { + bool ok = deployQmlImports(appDirPath, deploymentInfo, qmlDirs); + if (!ok && qmldirArgumentUsed) + return 1; // exit if the user explicitly asked for qml import deployment + // Update deploymentInfo.deployedLibraries - the QML imports + // may have brought in extra libraries as dependencies. + deploymentInfo.deployedLibraries += findAppLibraries(appDirPath); + deploymentInfo.deployedLibraries = deploymentInfo.deployedLibraries.toSet().toList(); + } + + deploymentInfo.usedModulesMask = 0; + findUsedModules(deploymentInfo); + + if (plugins && !deploymentInfo.qtPath.isEmpty()) { + if (deploymentInfo.pluginPath.isEmpty()) + deploymentInfo.pluginPath = QDir::cleanPath(deploymentInfo.qtPath + "/../plugins"); + deployPlugins(appDirPath, deploymentInfo); + createQtConf(appDirPath); + } + + if (runStripEnabled) + stripAppBinary(appDirPath); + + if (!skipTranslations) { + deployTranslations(appDirPath, deploymentInfo.usedModulesMask); + } + + if (appimage) { + int result = createAppImage(appDirPath); + LogDebug() << "result:" << result; + exit(result); + } + exit(0); } diff --git a/tools/shared/shared.cpp b/tools/linuxdeployqt/shared.cpp similarity index 100% rename from tools/shared/shared.cpp rename to tools/linuxdeployqt/shared.cpp diff --git a/tools/shared/shared.h b/tools/linuxdeployqt/shared.h similarity index 100% rename from tools/shared/shared.h rename to tools/linuxdeployqt/shared.h From 3bf8137ecb9a3b412d414fbf7d2e4ef5a49803d9 Mon Sep 17 00:00:00 2001 From: probonopd Date: Mon, 19 Jun 2017 18:27:16 +0200 Subject: [PATCH 4/5] 4 spaces instead of 1 tab to be consistent with previous code --- tools/linuxdeployqt/main.cpp | 766 +++++++++++++++++------------------ 1 file changed, 383 insertions(+), 383 deletions(-) diff --git a/tools/linuxdeployqt/main.cpp b/tools/linuxdeployqt/main.cpp index bf760a79..67baeccc 100644 --- a/tools/linuxdeployqt/main.cpp +++ b/tools/linuxdeployqt/main.cpp @@ -36,387 +36,387 @@ int main(int argc, char **argv) { - QCoreApplication app(argc, argv); - - extern QString appBinaryPath; - appBinaryPath = ""; // Cannot do it in one go due to "extern" - - QString firstArgument = QString::fromLocal8Bit(argv[1]); - - if (argc < 2 || firstArgument.startsWith("-")) { - qDebug() << "Usage: linuxdeployqt [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() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs)"; - qDebug() << " -no-strip : Don't run 'strip' on the binaries"; - qDebug() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries"; - qDebug() << " -executable= : Let the given executable use the deployed libraries too"; - qDebug() << " -qmldir= : Scan for QML imports in the given path"; - qDebug() << " -always-overwrite : Copy files even if the target file exists"; - qDebug() << " -no-translations : Skip deployment of translations."; - qDebug() << ""; - qDebug() << "linuxdeployqt takes an application as input and makes it"; - qDebug() << "self-contained by copying in the Qt libraries and plugins that"; - qDebug() << "the application uses."; - qDebug() << ""; - qDebug() << "It deploys the Qt instance that qmake on the $PATH points to,"; - qDebug() << "so make sure that it is the correct one."; - qDebug() << ""; - qDebug() << "Plugins related to a Qt library are copied in with the library."; - /* TODO: To be implemented - qDebug() << "The accessibility, image formats, and text codec"; - qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; - */ - qDebug() << ""; - qDebug() << "See the \"Deploying Applications on Linux\" topic in the"; - qDebug() << "documentation for more information about deployment on Linux."; - - return 1; - } - - QString desktopFile = ""; - QString desktopExecEntry = ""; - QString desktopIconEntry = ""; - - if (argc > 1) { - /* If we got a desktop file as the argument, try to figure out the application binary from it. - * This has the advantage that we can also figure out the icon file this way, and have less work - * to do when using linuxdeployqt. */ - if (firstArgument.endsWith(".desktop")){ - qDebug() << "Desktop file as first argument:" << firstArgument; - QSettings * settings = 0; - settings = new QSettings(firstArgument, QSettings::IniFormat); - desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed(); - qDebug() << "desktopExecEntry:" << desktopExecEntry; - desktopFile = firstArgument; - desktopIconEntry = settings->value("Desktop Entry/Icon", "r").toString().split(' ').first().split('.').first().trimmed(); - qDebug() << "desktopIconEntry:" << desktopIconEntry; - - QString candidateBin = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + desktopExecEntry); // Not FHS-like - - /* Search directory for an executable with the name in the Exec= key */ - QString directoryToBeSearched; - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath()); - - QDirIterator it(directoryToBeSearched, QDirIterator::Subdirectories); - while (it.hasNext()) { - it.next(); - if((it.fileName() == desktopExecEntry) && (it.fileInfo().isFile()) && (it.fileInfo().isExecutable())){ - qDebug() << "Found binary from desktop file:" << it.fileInfo().canonicalFilePath(); - appBinaryPath = it.fileInfo().absoluteFilePath(); - break; - } - } - - /* Only if we could not find it below the directory in which the desktop file resides, search above */ - if(appBinaryPath == ""){ - if(QFileInfo(QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../bin/" + desktopExecEntry)).exists()){ - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../"); - } else { - directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../"); - } - QDirIterator it2(directoryToBeSearched, QDirIterator::Subdirectories); - while (it2.hasNext()) { - it2.next(); - if((it2.fileName() == desktopExecEntry) && (it2.fileInfo().isFile()) && (it2.fileInfo().isExecutable())){ - qDebug() << "Found binary from desktop file:" << it2.fileInfo().canonicalFilePath(); - appBinaryPath = it2.fileInfo().absoluteFilePath(); - break; - } - } - } - - if(appBinaryPath == ""){ - if((QFileInfo(candidateBin).isFile()) && (QFileInfo(candidateBin).isExecutable())) { - appBinaryPath = QFileInfo(candidateBin).absoluteFilePath(); - } else { - LogError() << "Could not determine the path to the executable based on the desktop file\n"; - return 1; - } - } - - } else { - appBinaryPath = firstArgument; - appBinaryPath = QFileInfo(QDir::cleanPath(appBinaryPath)).absoluteFilePath(); - } - } - - // Allow binaries next to linuxdeployqt to be found; this is useful for bundling - // this application itself together with helper binaries such as patchelf - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString oldPath = env.value("PATH"); - QString newPath = QCoreApplication::applicationDirPath() + ":" + oldPath; - LogDebug() << newPath; - setenv("PATH",newPath.toUtf8().constData(),1); - - QString appName = QDir::cleanPath(QFileInfo(appBinaryPath).completeBaseName()); - - QString appDir = QDir::cleanPath(appBinaryPath + "/../"); - if (QDir().exists(appDir) == false) { - qDebug() << "Error: Could not find AppDir" << appDir; - return 1; - } - - bool plugins = true; - bool appimage = false; - extern bool runStripEnabled; - extern bool bundleAllButCoreLibs; - extern bool fhsLikeMode; - extern QString fhsPrefix; - extern bool alwaysOwerwriteEnabled; - extern QStringList librarySearchPath; - QStringList additionalExecutables; - bool qmldirArgumentUsed = false; - bool skipTranslations = false; - QStringList qmlDirs; - - /* FHS-like mode is for an application that has been installed to a $PREFIX which is otherwise empty, e.g., /path/to/usr. - * In this case, we want to construct an AppDir in /path/to. */ - if (QDir().exists((QDir::cleanPath(appBinaryPath + "/../../bin"))) == true) { - fhsPrefix = QDir::cleanPath(appBinaryPath + "/../../"); - qDebug() << "FHS-like mode with PREFIX, fhsPrefix:" << fhsPrefix; - fhsLikeMode = true; - } else { - qDebug() << "Not using FHS-like mode"; - } - - if (QDir().exists(appBinaryPath)) { - qDebug() << "app-binary:" << appBinaryPath; - } else { - qDebug() << "Error: Could not find app-binary" << appBinaryPath; - return 1; - } - - QString appDirPath; - QString relativeBinPath; - if(fhsLikeMode == false){ - appDirPath = appDir; - relativeBinPath = appName; - } else { - appDirPath = QDir::cleanPath(fhsPrefix + "/../"); - QString relativePrefix = fhsPrefix.replace(appDirPath+"/", ""); - relativeBinPath = relativePrefix + "/bin/" + appName; - } - qDebug() << "appDirPath:" << appDirPath; - qDebug() << "relativeBinPath:" << relativeBinPath; - - QFile appRun(appDirPath + "/AppRun"); - if(appRun.exists()){ - appRun.remove(); - } - - QFile::link(relativeBinPath, appDirPath + "/AppRun"); - - /* Copy the desktop file in place, into the top level of the AppDir */ - if(desktopFile != ""){ - QString destination = QDir::cleanPath(appDirPath + "/" + QFileInfo(desktopFile).fileName()); - if(QFileInfo(destination).exists() == false){ - if (QFile::copy(desktopFile, destination)){ - qDebug() << "Copied" << desktopFile << "to" << destination; - } - } - if(QFileInfo(destination).isFile() == false){ - LogError() << destination << "does not exist and could not be copied there\n"; - return 1; - } - } - - /* To make an AppDir, we need to find the icon and copy it in place */ - QStringList candidates; - QString iconToBeUsed = ""; - if(desktopIconEntry != ""){ - QDirIterator it3(appDirPath, QDirIterator::Subdirectories); - while (it3.hasNext()) { - it3.next(); - if((it3.fileName().startsWith(desktopIconEntry)) && ((it3.fileName().endsWith(".png")) || (it3.fileName().endsWith(".svg")) || (it3.fileName().endsWith(".svgz")) || (it3.fileName().endsWith(".xpm")))){ - candidates.append(it3.filePath()); - } - } - qDebug() << "Found icons from desktop file:" << candidates; - - /* Select the main icon from the candidates */ - if(candidates.length() == 1){ - iconToBeUsed = candidates.at(0); // The only choice - } else if(candidates.length() > 1){ - foreach(QString current, candidates) { - if(current.contains("256")){ - iconToBeUsed = current; - continue; - } - if(current.contains("128")){ - iconToBeUsed = current; - continue; - } - if(current.contains("svg")){ - iconToBeUsed = current; - continue; - } - if(current.contains("svgz")){ - iconToBeUsed = current; - continue; - } - if(current.contains("512")){ - iconToBeUsed = current; - continue; - } - if(current.contains("1024")){ - iconToBeUsed = current; - continue; - } - if(current.contains("64")){ - iconToBeUsed = current; - continue; - } - if(current.contains("48")){ - iconToBeUsed = current; - continue; - } - if(current.contains("xpm")){ - iconToBeUsed = current; - continue; - } - } - } - - /* Copy in place */ - if(iconToBeUsed != ""){ - /* Check if there is already an icon and only if there is not, copy it to the AppDir. - * As per the ROX AppDir spec, also copying to .DirIcon. */ - QString preExistingToplevelIcon = ""; - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".xpm").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".xpm"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svgz").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svgz"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".png").exists() == true){ - preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".png"; - if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); - } - - if(preExistingToplevelIcon != ""){ - qDebug() << "preExistingToplevelIcon:" << preExistingToplevelIcon; - } else { - qDebug() << "iconToBeUsed:" << iconToBeUsed; - QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); - if (QFile::copy(iconToBeUsed, targetIconPath)){ - qDebug() << "Copied" << iconToBeUsed << "to" << targetIconPath; - QFile::copy(targetIconPath, appDirPath + "/.DirIcon"); - } else { - LogError() << "Could not copy" << iconToBeUsed << "to" << targetIconPath << "\n"; - exit(1); - } - } - } - } - - 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("-appimage")) { - LogDebug() << "Argument found:" << argument; - appimage = true; - bundleAllButCoreLibs = true; - } else if (argument == QByteArray("-no-strip")) { - LogDebug() << "Argument found:" << argument; - runStripEnabled = false; - } else if (argument == QByteArray("-bundle-non-qt-libs")) { - LogDebug() << "Argument found:" << argument; - bundleAllButCoreLibs = true; - } 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 == QByteArray("-always-overwrite")) { - LogDebug() << "Argument found:" << argument; - alwaysOwerwriteEnabled = true; - } else if (argument == QByteArray("-no-translations")) { - LogDebug() << "Argument found:" << argument; - skipTranslations = true; - } else if (argument.startsWith("-")) { - LogError() << "Unknown argument" << argument << "\n"; - return 1; - } - } - - if (appimage) { - if(checkAppImagePrerequisites(appDirPath) == false){ - LogError() << "checkAppImagePrerequisites failed\n"; - return 1; - } - } - - DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables); - - // Convenience: Look for .qml files in the current directoty if no -qmldir specified. - if (qmlDirs.isEmpty()) { - QDir dir; - if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) { - qmlDirs += QStringLiteral("."); - } - } - - if (!qmlDirs.isEmpty()) { - bool ok = deployQmlImports(appDirPath, deploymentInfo, qmlDirs); - if (!ok && qmldirArgumentUsed) - return 1; // exit if the user explicitly asked for qml import deployment - // Update deploymentInfo.deployedLibraries - the QML imports - // may have brought in extra libraries as dependencies. - deploymentInfo.deployedLibraries += findAppLibraries(appDirPath); - deploymentInfo.deployedLibraries = deploymentInfo.deployedLibraries.toSet().toList(); - } - - deploymentInfo.usedModulesMask = 0; - findUsedModules(deploymentInfo); - - if (plugins && !deploymentInfo.qtPath.isEmpty()) { - if (deploymentInfo.pluginPath.isEmpty()) - deploymentInfo.pluginPath = QDir::cleanPath(deploymentInfo.qtPath + "/../plugins"); - deployPlugins(appDirPath, deploymentInfo); - createQtConf(appDirPath); - } - - if (runStripEnabled) - stripAppBinary(appDirPath); - - if (!skipTranslations) { - deployTranslations(appDirPath, deploymentInfo.usedModulesMask); - } - - if (appimage) { - int result = createAppImage(appDirPath); - LogDebug() << "result:" << result; - exit(result); - } - exit(0); + QCoreApplication app(argc, argv); + + extern QString appBinaryPath; + appBinaryPath = ""; // Cannot do it in one go due to "extern" + + QString firstArgument = QString::fromLocal8Bit(argv[1]); + + if (argc < 2 || firstArgument.startsWith("-")) { + qDebug() << "Usage: linuxdeployqt [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() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs)"; + qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries"; + qDebug() << " -executable= : Let the given executable use the deployed libraries too"; + qDebug() << " -qmldir= : Scan for QML imports in the given path"; + qDebug() << " -always-overwrite : Copy files even if the target file exists"; + qDebug() << " -no-translations : Skip deployment of translations."; + qDebug() << ""; + qDebug() << "linuxdeployqt takes an application as input and makes it"; + qDebug() << "self-contained by copying in the Qt libraries and plugins that"; + qDebug() << "the application uses."; + qDebug() << ""; + qDebug() << "It deploys the Qt instance that qmake on the $PATH points to,"; + qDebug() << "so make sure that it is the correct one."; + qDebug() << ""; + qDebug() << "Plugins related to a Qt library are copied in with the library."; + /* TODO: To be implemented + qDebug() << "The accessibility, image formats, and text codec"; + qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified."; + */ + qDebug() << ""; + qDebug() << "See the \"Deploying Applications on Linux\" topic in the"; + qDebug() << "documentation for more information about deployment on Linux."; + + return 1; + } + + QString desktopFile = ""; + QString desktopExecEntry = ""; + QString desktopIconEntry = ""; + + if (argc > 1) { + /* If we got a desktop file as the argument, try to figure out the application binary from it. + * This has the advantage that we can also figure out the icon file this way, and have less work + * to do when using linuxdeployqt. */ + if (firstArgument.endsWith(".desktop")){ + qDebug() << "Desktop file as first argument:" << firstArgument; + QSettings * settings = 0; + settings = new QSettings(firstArgument, QSettings::IniFormat); + desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed(); + qDebug() << "desktopExecEntry:" << desktopExecEntry; + desktopFile = firstArgument; + desktopIconEntry = settings->value("Desktop Entry/Icon", "r").toString().split(' ').first().split('.').first().trimmed(); + qDebug() << "desktopIconEntry:" << desktopIconEntry; + + QString candidateBin = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + desktopExecEntry); // Not FHS-like + + /* Search directory for an executable with the name in the Exec= key */ + QString directoryToBeSearched; + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath()); + + QDirIterator it(directoryToBeSearched, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + if((it.fileName() == desktopExecEntry) && (it.fileInfo().isFile()) && (it.fileInfo().isExecutable())){ + qDebug() << "Found binary from desktop file:" << it.fileInfo().canonicalFilePath(); + appBinaryPath = it.fileInfo().absoluteFilePath(); + break; + } + } + + /* Only if we could not find it below the directory in which the desktop file resides, search above */ + if(appBinaryPath == ""){ + if(QFileInfo(QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../bin/" + desktopExecEntry)).exists()){ + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../../"); + } else { + directoryToBeSearched = QDir::cleanPath(QFileInfo(firstArgument).absolutePath() + "/../"); + } + QDirIterator it2(directoryToBeSearched, QDirIterator::Subdirectories); + while (it2.hasNext()) { + it2.next(); + if((it2.fileName() == desktopExecEntry) && (it2.fileInfo().isFile()) && (it2.fileInfo().isExecutable())){ + qDebug() << "Found binary from desktop file:" << it2.fileInfo().canonicalFilePath(); + appBinaryPath = it2.fileInfo().absoluteFilePath(); + break; + } + } + } + + if(appBinaryPath == ""){ + if((QFileInfo(candidateBin).isFile()) && (QFileInfo(candidateBin).isExecutable())) { + appBinaryPath = QFileInfo(candidateBin).absoluteFilePath(); + } else { + LogError() << "Could not determine the path to the executable based on the desktop file\n"; + return 1; + } + } + + } else { + appBinaryPath = firstArgument; + appBinaryPath = QFileInfo(QDir::cleanPath(appBinaryPath)).absoluteFilePath(); + } + } + + // Allow binaries next to linuxdeployqt to be found; this is useful for bundling + // this application itself together with helper binaries such as patchelf + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString oldPath = env.value("PATH"); + QString newPath = QCoreApplication::applicationDirPath() + ":" + oldPath; + LogDebug() << newPath; + setenv("PATH",newPath.toUtf8().constData(),1); + + QString appName = QDir::cleanPath(QFileInfo(appBinaryPath).completeBaseName()); + + QString appDir = QDir::cleanPath(appBinaryPath + "/../"); + if (QDir().exists(appDir) == false) { + qDebug() << "Error: Could not find AppDir" << appDir; + return 1; + } + + bool plugins = true; + bool appimage = false; + extern bool runStripEnabled; + extern bool bundleAllButCoreLibs; + extern bool fhsLikeMode; + extern QString fhsPrefix; + extern bool alwaysOwerwriteEnabled; + extern QStringList librarySearchPath; + QStringList additionalExecutables; + bool qmldirArgumentUsed = false; + bool skipTranslations = false; + QStringList qmlDirs; + + /* FHS-like mode is for an application that has been installed to a $PREFIX which is otherwise empty, e.g., /path/to/usr. + * In this case, we want to construct an AppDir in /path/to. */ + if (QDir().exists((QDir::cleanPath(appBinaryPath + "/../../bin"))) == true) { + fhsPrefix = QDir::cleanPath(appBinaryPath + "/../../"); + qDebug() << "FHS-like mode with PREFIX, fhsPrefix:" << fhsPrefix; + fhsLikeMode = true; + } else { + qDebug() << "Not using FHS-like mode"; + } + + if (QDir().exists(appBinaryPath)) { + qDebug() << "app-binary:" << appBinaryPath; + } else { + qDebug() << "Error: Could not find app-binary" << appBinaryPath; + return 1; + } + + QString appDirPath; + QString relativeBinPath; + if(fhsLikeMode == false){ + appDirPath = appDir; + relativeBinPath = appName; + } else { + appDirPath = QDir::cleanPath(fhsPrefix + "/../"); + QString relativePrefix = fhsPrefix.replace(appDirPath+"/", ""); + relativeBinPath = relativePrefix + "/bin/" + appName; + } + qDebug() << "appDirPath:" << appDirPath; + qDebug() << "relativeBinPath:" << relativeBinPath; + + QFile appRun(appDirPath + "/AppRun"); + if(appRun.exists()){ + appRun.remove(); + } + + QFile::link(relativeBinPath, appDirPath + "/AppRun"); + + /* Copy the desktop file in place, into the top level of the AppDir */ + if(desktopFile != ""){ + QString destination = QDir::cleanPath(appDirPath + "/" + QFileInfo(desktopFile).fileName()); + if(QFileInfo(destination).exists() == false){ + if (QFile::copy(desktopFile, destination)){ + qDebug() << "Copied" << desktopFile << "to" << destination; + } + } + if(QFileInfo(destination).isFile() == false){ + LogError() << destination << "does not exist and could not be copied there\n"; + return 1; + } + } + + /* To make an AppDir, we need to find the icon and copy it in place */ + QStringList candidates; + QString iconToBeUsed = ""; + if(desktopIconEntry != ""){ + QDirIterator it3(appDirPath, QDirIterator::Subdirectories); + while (it3.hasNext()) { + it3.next(); + if((it3.fileName().startsWith(desktopIconEntry)) && ((it3.fileName().endsWith(".png")) || (it3.fileName().endsWith(".svg")) || (it3.fileName().endsWith(".svgz")) || (it3.fileName().endsWith(".xpm")))){ + candidates.append(it3.filePath()); + } + } + qDebug() << "Found icons from desktop file:" << candidates; + + /* Select the main icon from the candidates */ + if(candidates.length() == 1){ + iconToBeUsed = candidates.at(0); // The only choice + } else if(candidates.length() > 1){ + foreach(QString current, candidates) { + if(current.contains("256")){ + iconToBeUsed = current; + continue; + } + if(current.contains("128")){ + iconToBeUsed = current; + continue; + } + if(current.contains("svg")){ + iconToBeUsed = current; + continue; + } + if(current.contains("svgz")){ + iconToBeUsed = current; + continue; + } + if(current.contains("512")){ + iconToBeUsed = current; + continue; + } + if(current.contains("1024")){ + iconToBeUsed = current; + continue; + } + if(current.contains("64")){ + iconToBeUsed = current; + continue; + } + if(current.contains("48")){ + iconToBeUsed = current; + continue; + } + if(current.contains("xpm")){ + iconToBeUsed = current; + continue; + } + } + } + + /* Copy in place */ + if(iconToBeUsed != ""){ + /* Check if there is already an icon and only if there is not, copy it to the AppDir. + * As per the ROX AppDir spec, also copying to .DirIcon. */ + QString preExistingToplevelIcon = ""; + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".xpm").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".xpm"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svgz").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svgz"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".png").exists() == true){ + preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".png"; + if(QFileInfo(appDirPath + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon"); + } + + if(preExistingToplevelIcon != ""){ + qDebug() << "preExistingToplevelIcon:" << preExistingToplevelIcon; + } else { + qDebug() << "iconToBeUsed:" << iconToBeUsed; + QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); + if (QFile::copy(iconToBeUsed, targetIconPath)){ + qDebug() << "Copied" << iconToBeUsed << "to" << targetIconPath; + QFile::copy(targetIconPath, appDirPath + "/.DirIcon"); + } else { + LogError() << "Could not copy" << iconToBeUsed << "to" << targetIconPath << "\n"; + exit(1); + } + } + } + } + + 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("-appimage")) { + LogDebug() << "Argument found:" << argument; + appimage = true; + bundleAllButCoreLibs = true; + } else if (argument == QByteArray("-no-strip")) { + LogDebug() << "Argument found:" << argument; + runStripEnabled = false; + } else if (argument == QByteArray("-bundle-non-qt-libs")) { + LogDebug() << "Argument found:" << argument; + bundleAllButCoreLibs = true; + } 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 == QByteArray("-always-overwrite")) { + LogDebug() << "Argument found:" << argument; + alwaysOwerwriteEnabled = true; + } else if (argument == QByteArray("-no-translations")) { + LogDebug() << "Argument found:" << argument; + skipTranslations = true; + } else if (argument.startsWith("-")) { + LogError() << "Unknown argument" << argument << "\n"; + return 1; + } + } + + if (appimage) { + if(checkAppImagePrerequisites(appDirPath) == false){ + LogError() << "checkAppImagePrerequisites failed\n"; + return 1; + } + } + + DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables); + + // Convenience: Look for .qml files in the current directoty if no -qmldir specified. + if (qmlDirs.isEmpty()) { + QDir dir; + if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) { + qmlDirs += QStringLiteral("."); + } + } + + if (!qmlDirs.isEmpty()) { + bool ok = deployQmlImports(appDirPath, deploymentInfo, qmlDirs); + if (!ok && qmldirArgumentUsed) + return 1; // exit if the user explicitly asked for qml import deployment + // Update deploymentInfo.deployedLibraries - the QML imports + // may have brought in extra libraries as dependencies. + deploymentInfo.deployedLibraries += findAppLibraries(appDirPath); + deploymentInfo.deployedLibraries = deploymentInfo.deployedLibraries.toSet().toList(); + } + + deploymentInfo.usedModulesMask = 0; + findUsedModules(deploymentInfo); + + if (plugins && !deploymentInfo.qtPath.isEmpty()) { + if (deploymentInfo.pluginPath.isEmpty()) + deploymentInfo.pluginPath = QDir::cleanPath(deploymentInfo.qtPath + "/../plugins"); + deployPlugins(appDirPath, deploymentInfo); + createQtConf(appDirPath); + } + + if (runStripEnabled) + stripAppBinary(appDirPath); + + if (!skipTranslations) { + deployTranslations(appDirPath, deploymentInfo.usedModulesMask); + } + + if (appimage) { + int result = createAppImage(appDirPath); + LogDebug() << "result:" << result; + exit(result); + } + exit(0); } From 61b46173017f470a0c23972542480a074f6a946d Mon Sep 17 00:00:00 2001 From: probonopd Date: Mon, 19 Jun 2017 18:31:55 +0200 Subject: [PATCH 5/5] Add instructions "if you want to install linuxdeployqt into your Qt installation" --- BUILDING.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index b66838ca..23e2a1cf 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -12,10 +12,16 @@ git clone https://github.com/probonopd/linuxdeployqt.git # Then build in Qt Creator, or use export PATH=$(readlink -f /tmp/.mount_QtCreator-*-x86_64/*/gcc_64/bin/):$PATH cd linuxdeployqt -qmake linuxdeployqt.pro +qmake make ``` +* Optional if you want to install `linuxdeployqt` into your Qt installation, and make it a part of your Qt just like any other tool (qmake, etc.) + +``` +sudo make install +``` + * Build and install [patchelf](https://nixos.org/patchelf.html) (a small utility to modify the dynamic linker and RPATH of ELF executables; similar to `install_name_tool` on macOS). To learn more about this, see http://blog.qt.io/blog/2011/10/28/rpath-and-runpath/ ```