Browse Source

Merge branch 'master' into master

master
TheAssassin 7 years ago
committed by GitHub
parent
commit
d9413b37f1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      README.md
  2. 5
      tests/tests-environment.sh
  3. 18
      tests/tests.sh
  4. 20
      tools/linuxdeployqt/main.cpp
  5. 159
      tools/linuxdeployqt/shared.cpp
  6. 4
      tools/linuxdeployqt/shared.h

23
README.md

@ -30,6 +30,7 @@ Options:
-executable=<path> : Let the given executable use the deployed libraries too
-qmldir=<path> : Scan for QML imports to bundle from the given directory, determined by Qt's qmlimportscanner
-always-overwrite : Copy files even if the target file exists
-qmake=<path> : The qmake executable to use
-no-translations : Skip deployment of translations
linuxdeployqt takes an application as input and makes it
@ -54,7 +55,7 @@ Open in Qt Creator and build your application. Run it from the command line and
#### QMake configuration
__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
@ -62,8 +63,11 @@ qmake -v
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.
#### Remove unecessary files
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:
@ -87,16 +91,16 @@ dist: trusty
before_install:
- sudo add-apt-repository ppa:beineri/opt-qt59-trusty -y
- sudo apt-get update -qq
install:
install:
- sudo apt-get -y install qt59base
- source /opt/qt*/bin/qt*-env.sh
script:
- qmake PREFIX=/usr
- qmake CONFIG+=release PREFIX=/usr
- make -j$(nproc)
- make INSTALL_ROOT=appdir install ; find appdir/
- wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
- make INSTALL_ROOT=appdir -j$(nproc) install ; find appdir/
- 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
@ -105,7 +109,7 @@ script:
after_success:
- 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.
@ -127,9 +131,9 @@ If `qmake` does not allow for `make install` or does not install the desktop fil
__CMake__ wants `DESTDIR` instead:
```
- cmake . -DCMAKE_INSTALL_PREFIX=/usr
- cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
- make -j$(nproc)
- make DESTDIR=appdir install ; find appdir/
- make DESTDIR=appdir -j$(nproc) install ; find appdir/
```
__autotools__ (the dinosaur that spends precious minutes "checking...") wants `DESTDIR` too but insists on an absolute link which we can feed it using readlink:
@ -171,6 +175,7 @@ Providing an [AppImage](http://appimage.org/) would have, among others, these ad
- Can optionally GPG2-sign your AppImages (inside the file)
- Works on Live ISOs
- Can use the same AppImages when dual-booting multiple distributions
- Can be listed in the [AppImageHub](https://appimage.github.io/) central directory of available AppImages
[Here is an overview](https://github.com/probonopd/AppImageKit/wiki/AppImages) of projects that are already distributing upstream-provided, official AppImages.

5
tests/tests-environment.sh

@ -2,10 +2,11 @@
set -e
sudo add-apt-repository --yes ppa:beineri/opt-qt59-trusty
sudo add-apt-repository --yes ppa:beineri/opt-qt591-trusty
sudo apt-get update -qq
wget http://ftp.de.debian.org/debian/pool/main/p/patchelf/patchelf_0.8-2_amd64.deb
wget https://ftp.fau.de/debian/pool/main/p/patchelf/patchelf_0.8-2_amd64.deb
echo "5d506507df7c02766ae6c3ca0d15b4234f4cb79a80799190ded9d3ca0ac28c0c patchelf_0.8-2_amd64.deb" | sha256sum -c
sudo dpkg -i patchelf_0.8-2_amd64.deb
cd /tmp/

18
tests/tests.sh

@ -79,6 +79,24 @@ killall QtWidgetsApplication && echo "SUCCESS"
cd ../../../
###############################################################################
# Test bundling the sample Qt Widgets Application passing in the qmake exe
###############################################################################
cd tests/QtWidgetsApplication/build/
mkdir -p explicitqmake
cp QtWidgetsApplication explicitqmake/
../../../linuxdeployqt-*-x86_64.AppImage explicitqmake/QtWidgetsApplication \
-qmake=$(which qmake)
ldd explicitqmake/QtWidgetsApplication
find explicitqmake/
LD_DEBUG=libs explicitqmake/QtWidgetsApplication &
sleep 5
killall QtWidgetsApplication && echo "SUCCESS"
cd ../../../
###############################################################################
# Test bundling the sample Qt Quick Controls 2 Application that comes with Qt Creator
###############################################################################

20
tools/linuxdeployqt/main.cpp

@ -55,14 +55,16 @@ int main(int argc, char **argv)
qDebug() << " -executable=<path> : Let the given executable use the deployed libraries too";
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
qDebug() << " -always-overwrite : Copy files even if the target file exists";
qDebug() << " -qmake=<path> : The qmake executable to use";
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() << "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
@ -86,6 +88,12 @@ int main(int argc, char **argv)
* to do when using linuxdeployqt. */
if (firstArgument.endsWith(".desktop")){
qDebug() << "Desktop file as first argument:" << firstArgument;
/* Check if the desktop file really exists */
if (! QFile::exists(firstArgument)) {
LogError() << "Desktop file in first argument does not exist!";
return 1;
}
QSettings * settings = 0;
settings = new QSettings(firstArgument, QSettings::IniFormat);
desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed();
@ -171,6 +179,7 @@ int main(int argc, char **argv)
bool qmldirArgumentUsed = false;
bool skipTranslations = 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. */
@ -365,6 +374,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 == QByteArray("-no-translations")) {
LogDebug() << "Argument found:" << argument;
skipTranslations = true;
@ -381,7 +394,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()) {

159
tools/linuxdeployqt/shared.cpp

@ -61,7 +61,7 @@ bool deployLibrary = false;
using std::cout;
using std::endl;
QMap<QString,QString> qtToBeBundledInfo;
static QMap<QString,QString> qtToBeBundledInfo;
enum QtModule
#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC)
@ -202,7 +202,7 @@ QDebug operator<<(QDebug debug, const LibraryInfo &info)
return debug;
}
QString bundleLibraryDirectory;
static QString bundleLibraryDirectory;
inline QDebug operator<<(QDebug debug, const AppDirInfo &info)
{
@ -217,20 +217,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)
@ -298,6 +298,14 @@ LddInfo findDependencyInfo(const QString &binaryPath)
}
foreach (QString outputLine, outputLines) {
if(outputLine.contains("libQt5")){
qtDetected = 5;
}
if(outputLine.contains("libQtCore.so.4")){
qtDetected = 4;
}
// LogDebug() << "ldd outputLine:" << outputLine;
if ((outputLine.contains("not found")) && (qtDetectionComplete == 1)){
LogError() << "ldd outputLine:" << outputLine.replace("\t", "");
@ -375,7 +383,7 @@ LibraryInfo parseLddLibraryLine(const QString &line, const QString &appDirPath,
*/
QStringList excludelist;
excludelist << "libasound.so.2" << "libcom_err.so.2" << "libcrypt.so.1" << "libc.so.6" << "libdl.so.2" << "libdrm.so.2" << "libexpat.so.1" << "libfontconfig.so.1" << "libgcc_s.so.1" << "libgdk_pixbuf-2.0.so.0" << "libgdk-x11-2.0.so.0" << "libgio-2.0.so.0" << "libglib-2.0.so.0" << "libGL.so.1" << "libgobject-2.0.so.0" << "libgpg-error.so.0" << "libgssapi_krb5.so.2" << "libgtk-x11-2.0.so.0" << "libICE.so.6" << "libidn.so.11" << "libk5crypto.so.3" << "libkeyutils.so.1" << "libm.so.6" << "libnss3.so" << "libnssutil3.so" << "libp11-kit.so.0" << "libpangoft2-1.0.so.0" << "libpangocairo-1.0.so.0" << "libpango-1.0.so.0" << "libpthread.so.0" << "libresolv.so.2" << "librt.so.1" << "libselinux.so.1" << "libSM.so.6" << "libstdc++.so.6" << "libusb-1.0.so.0" << "libuuid.so.1" << "libX11.so.6" << "libxcb.so.1" << "libz.so.1";
excludelist << "libasound.so.2" << "libcom_err.so.2" << "libcrypt.so.1" << "libc.so.6" << "libdl.so.2" << "libdrm.so.2" << "libexpat.so.1" << "libfontconfig.so.1" << "libgcc_s.so.1" << "libgdk_pixbuf-2.0.so.0" << "libgdk-x11-2.0.so.0" << "libgio-2.0.so.0" << "libglib-2.0.so.0" << "libGL.so.1" << "libgobject-2.0.so.0" << "libgpg-error.so.0" << "libgssapi_krb5.so.2" << "libgtk-x11-2.0.so.0" << "libICE.so.6" << "libidn.so.11" << "libk5crypto.so.3" << "libkeyutils.so.1" << "libm.so.6" << "libnss3.so" << "libnssutil3.so" << "libp11-kit.so.0" << "libpangoft2-1.0.so.0" << "libpangocairo-1.0.so.0" << "libpango-1.0.so.0" << "libpthread.so.0" << "libresolv.so.2" << "librt.so.1" << "libSM.so.6" << "libstdc++.so.6" << "libusb-1.0.so.0" << "libuuid.so.1" << "libX11.so.6" << "libxcb.so.1" << "libz.so.1";
LogDebug() << "excludelist:" << excludelist;
if (! trimmed.contains("libicu")) {
if (containsHowOften(excludelist, QFileInfo(trimmed).completeBaseName())) {
@ -749,16 +757,16 @@ void changeIdentification(const QString &id, const QString &binaryPath)
QString oldRpath = runPatchelf(QStringList() << "--print-rpath" << binaryPath);
LogDebug() << "oldRpath:" << oldRpath;
if (oldRpath.startsWith("/")){
LogDebug() << "Old rpath in" << binaryPath << "starts with /, hence adding it to LD_LIBRARY_PATH";
// FIXME: Split along ":" characters, check each one, only append to LD_LIBRARY_PATH if not already there
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString oldPath = env.value("LD_LIBRARY_PATH");
if (not oldPath.contains(oldRpath)){
QString newPath = oldRpath + ":" + oldPath; // FIXME: If we use a ldd replacement, we still need to observe this path
// FIXME: Directory layout might be different for system Qt; cannot assume lib/ to always be inside the Qt directory
LogDebug() << "Added to LD_LIBRARY_PATH:" << newPath;
setenv("LD_LIBRARY_PATH",newPath.toUtf8().constData(),1);
}
LogDebug() << "Old rpath in" << binaryPath << "starts with /, hence adding it to LD_LIBRARY_PATH";
// FIXME: Split along ":" characters, check each one, only append to LD_LIBRARY_PATH if not already there
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString oldPath = env.value("LD_LIBRARY_PATH");
if (not oldPath.contains(oldRpath)){
QString newPath = oldRpath + ":" + oldPath; // FIXME: If we use a ldd replacement, we still need to observe this path
// FIXME: Directory layout might be different for system Qt; cannot assume lib/ to always be inside the Qt directory
LogDebug() << "Added to LD_LIBRARY_PATH:" << newPath;
setenv("LD_LIBRARY_PATH",newPath.toUtf8().constData(),1);
}
}
LogNormal() << "Changing rpath in" << binaryPath << "to" << id;
runPatchelf(QStringList() << "--set-rpath" << id << binaryPath);
@ -912,8 +920,8 @@ DeploymentInfo deployQtLibraries(QList<LibraryInfo> libraries,
LogNormal() << "Setting deploymentInfo.qtPath to:" << library.libraryDirectory;
deploymentInfo.qtPath = library.libraryDirectory;
}
if(library.libraryName.contains("libQt") and library.libraryName.contains("Widgets.so")) {
if(library.libraryName.contains("libQt") and library.libraryName.contains("Widgets.so")) {
deploymentInfo.requiresQtWidgetsLibrary = true;
}
@ -974,7 +982,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;
@ -985,39 +993,32 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
// Find out whether Qt is a dependency of the application to be bundled
LddInfo lddInfo = findDependencyInfo(appBinaryPath);
foreach (const DylibInfo dep, lddInfo.dependencies) {
LogDebug() << "dep.binaryPath" << dep.binaryPath;
if(dep.binaryPath.contains("libQt5")){
qtDetected = 5;
}
if(dep.binaryPath.contains("libQtCore.so.4")){
qtDetected = 4;
}
}
if(qtDetected != 0){
// Determine the location of the Qt to be bundled
LogDebug() << "Using qmake to determine the location of the Qt to be bundled";
QString qmakePath = "";
// Try to find a version specific qmake first
// 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(qtDetected == 5){
qmakePath = QStandardPaths::findExecutable("qmake-qt5");
LogDebug() << "qmake 5";
} else if(qtDetected == 4){
qmakePath = QStandardPaths::findExecutable("qmake-qt4");
LogDebug() << "qmake 4";
}
// Use the qmake executable passed in by the user:
QString qmakePath = qmake;
if (qmakePath.isEmpty()) {
// Try to find a version specific qmake first
// 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(qtDetected == 5){
qmakePath = QStandardPaths::findExecutable("qmake-qt5");
LogDebug() << "qmake 5";
} else if(qtDetected == 4){
qmakePath = QStandardPaths::findExecutable("qmake-qt4");
LogDebug() << "qmake 4";
}
if(qmakePath == ""){
// The upstream name of the binary is "qmake", for Qt 4 and Qt 5
qmakePath = QStandardPaths::findExecutable("qmake");
if(qmakePath == ""){
// The upstream name of the binary is "qmake", for Qt 4 and Qt 5
qmakePath = QStandardPaths::findExecutable("qmake");
}
}
if(qmakePath == ""){
@ -1122,7 +1123,7 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
}
} else {
pluginList.append(QStringLiteral("imageformats/") + plugin);
}
}
}
}
@ -1132,8 +1133,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")) {
@ -1175,7 +1176,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
@ -1232,7 +1233,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) {
@ -1251,7 +1252,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());
}
}
}
@ -1263,23 +1264,23 @@ void createQtConf(const QString &appDirPath)
// See https://github.com/probonopd/linuxdeployqt/issues/ 75, 98, 99
QByteArray contents;
if(fhsLikeMode){
contents = "# Generated by linuxdeployqt\n"
"# https://github.com/probonopd/linuxdeployqt/\n"
"[Paths]\n"
"Prefix = ../\n"
"Plugins = plugins\n"
"Imports = qml\n"
"Qml2Imports = qml\n";
contents = "# Generated by linuxdeployqt\n"
"# https://github.com/probonopd/linuxdeployqt/\n"
"[Paths]\n"
"Prefix = ../\n"
"Plugins = plugins\n"
"Imports = qml\n"
"Qml2Imports = qml\n";
} else {
contents = "# Generated by linuxdeployqt\n"
"# https://github.com/probonopd/linuxdeployqt/\n"
"[Paths]\n"
"Prefix = ./\n"
"Plugins = plugins\n"
"Imports = qml\n"
"Qml2Imports = qml\n";
}
contents = "# Generated by linuxdeployqt\n"
"# https://github.com/probonopd/linuxdeployqt/\n"
"[Paths]\n"
"Prefix = ./\n"
"Plugins = plugins\n"
"Imports = qml\n"
"Qml2Imports = qml\n";
}
QString filePath = appDirPath + "/"; // Is picked up when placed next to the main executable
QString fileName = QDir::cleanPath(appBinaryPath + "/../qt.conf");
@ -1403,7 +1404,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;

4
tools/linuxdeployqt/shared.h

@ -114,7 +114,9 @@ QString findAppBinary(const QString &appDirPath);
QList<LibraryInfo> getQtLibraries(const QString &path, const QString &appDirPath, const QSet<QString> &rpaths);
QList<LibraryInfo> getQtLibraries(const QStringList &lddLines, const QString &appDirPath, const QSet<QString> &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<LibraryInfo> libraries,const QString &bundlePath, const QStringList &binaryPaths, bool useLoaderPath);
void createQtConf(const QString &appDirPath);
void createQtConfForQtWebEngineProcess(const QString &appDirPath);

Loading…
Cancel
Save