From a3a6376573fcb17a5f1e8528d2ac2789ec5f5f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20H=C3=B6her?= Date: Wed, 17 May 2017 21:30:22 +0200 Subject: [PATCH] Allow the qmake executable to be passed on CLI This change adds a new command line option `qmake` to the tool which allows the user to specify the qmake executable to be used. By default, if that option is omitted, the behavior is unchanged (i.e. the tool first searches for the `qmake` executable and - if this is not successful - then for either `qmake-qt5` or `qmake-qt4`. If the new option is used, no search takes place and instead the executable provided is used as-is. This implements a part of the functionality as discussed in issue #94. --- README.md | 11 +++++---- linuxdeployqt/main.cpp | 18 ++++++++++---- shared/shared.cpp | 56 ++++++++++++++++++++++-------------------- shared/shared.h | 4 ++- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 4083e69..eb88ef3 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Please download __linuxdeployqt-x86_64.AppImage__ from the [Releases](https://gi Open in Qt Creator and build your application. Run it from the command line and inspect it with `ldd` to make sure the correct libraries from the correct locations are getting loaded, as `linuxdeployqt` will use `ldd` internally to determine from where to copy libraries into the bundle. -__Important:__ `linuxdeployqt` deploys the Qt instance that qmake on the $PATH points to, so make sure that it is the correct one. Verify that qmake finds the correct Qt instance like this before running the `linuxdeployqt` tool: +__Important:__ By default, `linuxdeployqt` deploys the Qt instance that qmake on the $PATH points to, so make sure that it is the correct one. Verify that qmake finds the correct Qt instance like this before running the `linuxdeployqt` tool: ``` qmake -v @@ -29,6 +29,7 @@ QMake version 3.0 Using Qt version 5.7.0 in /tmp/.mount_QtCreator-5.7.0-x86_64/5.7/gcc_64/lib ``` If this does not show the correct path to your Qt instance that you want to be bundled, then adjust your `$PATH` to find the correct `qmake`. +Alternatively, use the `-qmake` command line option to point the tool directly to the qmake executable to be used. Before running linuxdeployqt it may be wise to delete unneeded files that you do not wish to distribute from the build directory. These may be autogenerated during the build. You can delete them like so: @@ -69,8 +70,8 @@ dist: trusty before_install: - sudo add-apt-repository ppa:beineri/opt-qt58-trusty -y - sudo apt-get update -qq - -install: + +install: - sudo apt-get -y install qt58base - source /opt/qt*/bin/qt*-env.sh @@ -80,14 +81,14 @@ script: - make INSTALL_ROOT=appdir install ; find appdir/ after_success: - - wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" + - wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - chmod a+x linuxdeployqt*.AppImage - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage - find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq - curl --upload-file ./APPNAME*.AppImage https://transfer.sh/APPNAME-git.$(git rev-parse --short HEAD)-x86_64.AppImage -``` +``` When you save your change, then Travis CI should build and upload an AppImage for you. More likely than not, some fine-tuning will still be required. diff --git a/linuxdeployqt/main.cpp b/linuxdeployqt/main.cpp index 0d203ae..e12040c 100644 --- a/linuxdeployqt/main.cpp +++ b/linuxdeployqt/main.cpp @@ -55,13 +55,15 @@ int main(int argc, char **argv) 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() << " -qmake= : The qmake executable to use"; 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() << "By default it deploys the Qt instance that qmake on the $PATH points to."; + qDebug() << "The '-qmake' option can be used to point to the qmake executable"; + qDebug() << "to be used instead."; qDebug() << ""; qDebug() << "Plugins related to a Qt library are copied in with the library."; /* TODO: To be implemented @@ -145,7 +147,7 @@ int main(int argc, char **argv) // 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 oldPath = env.value("PATH"); QString newPath = QCoreApplication::applicationDirPath() + ":" + oldPath; LogDebug() << newPath; setenv("PATH",newPath.toUtf8().constData(),1); @@ -169,6 +171,7 @@ int main(int argc, char **argv) QStringList additionalExecutables; bool qmldirArgumentUsed = false; QStringList qmlDirs; + QString qmakeExecutable; /* 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. */ @@ -304,7 +307,7 @@ int main(int argc, char **argv) qDebug() << "preExistingToplevelIcon:" << preExistingToplevelIcon; } else { qDebug() << "iconToBeUsed:" << iconToBeUsed; - QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); + QString targetIconPath = appDirPath + "/" + QFileInfo(iconToBeUsed).fileName(); if (QFile::copy(iconToBeUsed, targetIconPath)){ qDebug() << "Copied" << iconToBeUsed << "to" << targetIconPath; QFile::copy(targetIconPath, appDirPath + "/.DirIcon"); @@ -358,6 +361,10 @@ int main(int argc, char **argv) } else if (argument == QByteArray("-always-overwrite")) { LogDebug() << "Argument found:" << argument; alwaysOwerwriteEnabled = true; + } else if (argument.startsWith("-qmake=")) { + LogDebug() << "Argument found:" << argument; + int index = argument.indexOf("="); + qmakeExecutable = argument.mid(index+1); } else if (argument.startsWith("-")) { LogError() << "Unknown argument" << argument << "\n"; return 1; @@ -371,7 +378,8 @@ int main(int argc, char **argv) } } - DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables); + DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables, + qmakeExecutable); // Convenience: Look for .qml files in the current directoty if no -qmldir specified. if (qmlDirs.isEmpty()) { diff --git a/shared/shared.cpp b/shared/shared.cpp index c4e1e8f..82714fd 100644 --- a/shared/shared.cpp +++ b/shared/shared.cpp @@ -99,20 +99,20 @@ inline QDebug operator<<(QDebug debug, const AppDirInfo &info) // on architecture. See "vDSO names" in the notes section of vdso(7) // for more information. static bool lddOutputContainsLinuxVDSO(const QString &lddOutput) { - // aarch64, arm, mips, x86_64, x86/x32 - if (lddOutput.contains(QStringLiteral("linux-vdso.so.1"))) { - return true; - // ppc32, s390 - } else if (lddOutput.contains(QStringLiteral("linux-vdso32.so.1"))) { - return true; - // ppc64, s390x - } else if (lddOutput.contains(QStringLiteral("linux-vdso64.so.1"))) { - return true; - // ia64, sh, i386 - } else if (lddOutput.contains(QStringLiteral("linux-gate.so.1"))) { - return true; - } - return false; + // aarch64, arm, mips, x86_64, x86/x32 + if (lddOutput.contains(QStringLiteral("linux-vdso.so.1"))) { + return true; + // ppc32, s390 + } else if (lddOutput.contains(QStringLiteral("linux-vdso32.so.1"))) { + return true; + // ppc64, s390x + } else if (lddOutput.contains(QStringLiteral("linux-vdso64.so.1"))) { + return true; + // ia64, sh, i386 + } else if (lddOutput.contains(QStringLiteral("linux-gate.so.1"))) { + return true; + } + return false; } bool copyFilePrintStatus(const QString &from, const QString &to) @@ -879,7 +879,7 @@ static QString captureOutput(const QString &command) return process.readAllStandardOutput(); } -DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &additionalExecutables) +DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &additionalExecutables, const QString& qmake) { AppDirInfo applicationBundle; @@ -906,15 +906,19 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a // Determine the location of the Qt to be bundled LogDebug() << "Using qmake to determine the location of the Qt to be bundled"; - QString qmakePath = ""; + // Use the qmake executable passed in by the user: + QString qmakePath = qmake; - // The upstream name of the binary is "qmake", for Qt 4 and Qt 5 - qmakePath = QStandardPaths::findExecutable("qmake"); + // If we did not get a qmake, first try to find "qmake", which is the + // upstream name of the binary in both Qt4 and Qt5: + if (qmakePath.isEmpty()) { + qmakePath = QStandardPaths::findExecutable("qmake"); + } // But openSUSE has qmake for Qt 4 and qmake-qt5 for Qt 5 // Qt 4 on Fedora comes with suffix -qt4 // http://www.geopsy.org/wiki/index.php/Installing_Qt_binary_packages - if(qmakePath == ""){ + if(qmakePath.isEmpty()){ if(qtDetected == 5){ qmakePath = QStandardPaths::findExecutable("qmake-qt5"); } @@ -1016,7 +1020,7 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath } } else { pluginList.append(QStringLiteral("imageformats/") + plugin); - } + } } } @@ -1026,8 +1030,8 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath foreach (const QString &plugin, xcbglintegrationPlugins) { pluginList.append(QStringLiteral("xcbglintegrations/") + plugin); } - } - + } + // Also deploy plugins/iconengines/libqsvgicon.so whenever libQt5Svg.so.* is about to be deployed, // https://github.com/probonopd/linuxdeployqt/issues/36 if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Svg")) { @@ -1069,7 +1073,7 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath QString sourcePath; QString destinationPath; - + // Qt WebEngine if libQt5WebEngineCore is in use // https://doc-snapshots.qt.io/qt5-5.7/qtwebengine-deploying.html // TODO: Rather than hardcode the source paths, somehow get them dynamically @@ -1124,7 +1128,7 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath destinationPath = QDir::cleanPath(dstTranslations + "/qtwebengine_locales"); recursiveCopy(sourcePath, destinationPath); } - + LogNormal() << "pluginList after having detected hopefully all required plugins:" << pluginList; foreach (const QString &plugin, pluginList) { @@ -1143,7 +1147,7 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath QString relativePath = dir.relativeFilePath(appDirInfo.path + "/" + libraries[0].libraryDestinationDirectory); relativePath.remove(0, 3); // remove initial '../' changeIdentification("$ORIGIN/" + relativePath, QFileInfo(destinationPath).canonicalFilePath()); - + } } } @@ -1224,7 +1228,7 @@ bool deployQmlImports(const QString &appDirPath, DeploymentInfo deploymentInfo, argumentList.append(qtToBeBundledInfo.value("QT_INSTALL_QML")); LogDebug() << "qmlImportsPath (QT_INSTALL_QML):" << qtToBeBundledInfo.value("QT_INSTALL_QML"); - + // run qmlimportscanner QProcess qmlImportScanner; LogDebug() << qmlImportScannerPath << argumentList; diff --git a/shared/shared.h b/shared/shared.h index 9e0ff6d..82dcb61 100644 --- a/shared/shared.h +++ b/shared/shared.h @@ -112,7 +112,9 @@ QString findAppBinary(const QString &appDirPath); QList getQtLibraries(const QString &path, const QString &appDirPath, const QSet &rpaths); QList getQtLibraries(const QStringList &lddLines, const QString &appDirPath, const QSet &rpaths); QString copyLibrary(const LibraryInfo &library, const QString path); -DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &additionalExecutables); +DeploymentInfo deployQtLibraries(const QString &appDirPath, + const QStringList &additionalExecutables, + const QString &qmake); DeploymentInfo deployQtLibraries(QList libraries,const QString &bundlePath, const QStringList &binaryPaths, bool useLoaderPath); void deployPlugins(const QString &appDirPath, DeploymentInfo deploymentInfo); bool deployQmlImports(const QString &appDirPath, DeploymentInfo deploymentInfo, QStringList &qmlDirs);