Merge branch 'master' into find_qmake_in_exepath

This commit is contained in:
Henner Kollmann
2018-05-09 14:40:01 +02:00
17 changed files with 710 additions and 161 deletions
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
set -e
# download excludelist
blacklisted=($(wget --quiet https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | grep -v "^#.*" | grep "[^-\s]"))
# sanity check
if [ "$blacklisted" == "" ]; then
exit 1;
fi
filename=$(readlink -f $(dirname "$0"))/linuxdeployqt/excludelist.h
# overwrite existing source file
cat > "$filename" <<EOF
/*
* List of libraries to exclude for different reasons.
*
* Automatically generated from
* https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist
*
* This file shall be committed by the developers occassionally,
* otherwise systems without access to the internet won't be able to build
* fully working versions of linuxdeployqt.
*
* See https://github.com/probonopd/linuxdeployqt/issues/274 for more
* information.
*/
#include <QStringList>
static const QStringList generatedExcludelist = {
EOF
# Create array
for item in ${blacklisted[@]:0:${#blacklisted[@]}-1}; do
echo -e ' "'"$item"'",' >> "$filename"
done
echo -e ' "'"${blacklisted[$((${#blacklisted[@]}-1))]}"'"' >> "$filename"
echo "};" >> "$filename"
+30
View File
@@ -0,0 +1,30 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# expose version data as compiler definition
add_definitions("-DLINUXDEPLOYQT_VERSION=\"${GIT_TAG_NAME}\"")
add_definitions("-DLINUXDEPLOYQT_GIT_COMMIT=\"${GIT_COMMIT}\"")
add_definitions("-DBUILD_DATE=\"${DATE}\"")
add_definitions("-DBUILD_NUMBER=\"${BUILD_NUMBER}\"")
find_package(Qt5 REQUIRED COMPONENTS Core)
# update excludelist
message(STATUS "Updating excludelist...")
execute_process(
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/../generate-excludelist.sh
OUTPUT_VARIABLE EXCLUDELIST
TIMEOUT 10
RESULT_VARIABLE EXCLUDELIST_RESULT
)
if(NOT EXCLUDELIST_RESULT EQUAL 0)
message(WARNING "Updating excludelist failed, using outdated copy")
endif()
mark_as_advanced(EXCLUDELIST EXCLUDELIST_RESULT)
add_executable(linuxdeployqt main.cpp shared.cpp)
target_include_directories(linuxdeployqt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(linuxdeployqt Qt5::Core)
target_compile_definitions(linuxdeployqt PRIVATE -DEXCLUDELIST="${EXCLUDELIST}")
+69
View File
@@ -0,0 +1,69 @@
/*
* List of libraries to exclude for different reasons.
*
* Automatically generated from
* https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist
*
* This file shall be committed by the developers occassionally,
* otherwise systems without access to the internet won't be able to build
* fully working versions of linuxdeployqt.
*
* See https://github.com/probonopd/linuxdeployqt/issues/274 for more
* information.
*/
#include <QStringList>
static const QStringList generatedExcludelist = {
"ld-linux.so.2",
"ld-linux-x86-64.so.2",
"libanl.so.1",
"libasound.so.2",
"libBrokenLocale.so.1",
"libcidn.so.1",
"libcom_err.so.2",
"libcrypt.so.1",
"libc.so.6",
"libdl.so.2",
"libdrm.so.2",
"libexpat.so.1",
"libfontconfig.so.1",
"libfreetype.so.6",
"libgcc_s.so.1",
"libgdk_pixbuf-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",
"libharfbuzz.so.0",
"libICE.so.6",
"libjack.so.0",
"libkeyutils.so.1",
"libm.so.6",
"libmvec.so.1",
"libnsl.so.1",
"libnss_compat.so.2",
"libnss_db.so.2",
"libnss_dns.so.2",
"libnss_files.so.2",
"libnss_hesiod.so.2",
"libnss_nisplus.so.2",
"libnss_nis.so.2",
"libp11-kit.so.0",
"libpango-1.0.so.0",
"libpangocairo-1.0.so.0",
"libpangoft2-1.0.so.0",
"libpthread.so.0",
"libresolv.so.2",
"librt.so.1",
"libSM.so.6",
"libstdc++.so.6",
"libthread_db.so.1",
"libusb-1.0.so.0",
"libutil.so.1",
"libuuid.so.1",
"libX11.so.6",
"libxcb.so.1",
"libz.so.1"
};
+33 -1
View File
@@ -12,6 +12,38 @@ load(qt_tool)
HEADERS += shared.h
SOURCES += main.cpp \
shared.cpp
shared.cpp
DEFINES -= QT_USE_QSTRINGBUILDER #leads to compile errors if not disabled
# versioning
# don't break the quotes -- at the moment, the shell commands are injected into the Makefile to have them run on every
# build and not during configure time
DEFINES += LINUXDEPLOYQT_GIT_COMMIT="'\"$(shell cd $$PWD && git rev-parse --short HEAD)\"'"
DEFINES += BUILD_DATE="'\"$(shell env LC_ALL=C date -u '+%Y-%m-%d %H:%M:%S %Z')\"'"
_BUILD_NUMBER = $$(TRAVIS_BUILD_NUMBER)
isEmpty(_BUILD_NUMBER) {
message("Not building on Travis CI, tagging build as local dev build")
DEFINES += BUILD_NUMBER="'\"<local dev build>\"'"
} else {
message("Building on Travis CI build, build number $$_BUILD_NUMBER")
DEFINES += BUILD_NUMBER="'\"$$_BUILD_NUMBER\"'"
}
DEFINES += LINUXDEPLOYQT_VERSION="'\"$(shell cd $$PWD && git describe --tags $(shell cd $$PWD && git rev-list --tags --skip=1 --max-count=1) --abbrev=0)\"'"
contains(DEFINES, EXCLUDELIST.*) {
message("EXCLUDELIST specified, to use the most recent exclude list, please run qmake without EXCLUDELIST definition and with internet.")
} else {
message("Updating exclude list...")
# check whether command _would_ run successfully
EXCLUDELIST_GENERATION_WORKS = FALSE
system($$_PRO_FILE_PWD_/../generate-excludelist.sh): EXCLUDELIST_GENERATION_WORKS = TRUE
isEqual(EXCLUDELIST_GENERATION_WORKS, FALSE) {
warning("Updating excludelist failed, using outdated copy")
}
}
+97 -33
View File
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd. and Simon Peter
** Copyright (C) 2016-18 The Qt Company Ltd. and Simon Peter
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -33,6 +33,8 @@
#include <stdlib.h>
#include <QSettings>
#include <QDirIterator>
#include <sstream>
#include "excludelist.h"
int main(int argc, char **argv)
{
@@ -43,37 +45,69 @@ int main(int argc, char **argv)
QString firstArgument = QString::fromLocal8Bit(argv[1]);
if (argc < 2 || firstArgument.startsWith("-")) {
qDebug() << "Usage: linuxdeployqt <app-binary|desktop file> [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=<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() << "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.";
// print version statement
std::stringstream version;
version << "linuxdeployqt " << LINUXDEPLOYQT_VERSION
<< " (commit " << LINUXDEPLOYQT_GIT_COMMIT << "), "
<< "build " << BUILD_NUMBER << " built on " << BUILD_DATE;
qInfo().noquote() << QString::fromStdString(version.str());
// due to the structure of the argument parser, we have to check all arguments at first to check whether the user
// wants to get the version only
// TODO: replace argument parser with position independent, less error prone version
for (int i = 0; i < argc; i++ ) {
QString argument = argv[i];
if (argument == "-version" || argument == "-V" || argument == "--version") {
// can just exit normally, version has been printed above
return 0;
}
if (argument == QByteArray("-show-exclude-libs")) {
qInfo() << generatedExcludelist;
return 0;
}
}
if (argc < 2 || (firstArgument.startsWith("-"))) {
qInfo() << "";
qInfo() << "Usage: linuxdeployqt <app-binary|desktop file> [options]";
qInfo() << "";
qInfo() << "Options:";
qInfo() << " -always-overwrite : Copy files even if the target file exists.";
qInfo() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs).";
qInfo() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries.";
qInfo() << " -exclude-libs=<list> : List of libraries which should be excluded,";
qInfo() << " separated by comma.";
qInfo() << " -executable=<path> : Let the given executable use the deployed libraries";
qInfo() << " too";
qInfo() << " -extra-plugins=<list> : List of extra plugins which should be deployed,";
qInfo() << " separated by comma.";
qInfo() << " -no-copy-copyright-files : Skip deployment of copyright files.";
qInfo() << " -no-plugins : Skip plugin deployment.";
qInfo() << " -no-strip : Don't run 'strip' on the binaries.";
qInfo() << " -no-translations : Skip deployment of translations.";
qInfo() << " -qmake=<path> : The qmake executable to use.";
qInfo() << " -qmldir=<path> : Scan for QML imports in the given path.";
qInfo() << " -show-exclude-libs : Print exclude libraries list.";
qInfo() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default),";
qInfo() << " 2 = normal, 3 = debug.";
qInfo() << " -version : Print version statement and exit.";
qInfo() << "";
qInfo() << "linuxdeployqt takes an application as input and makes it";
qInfo() << "self-contained by copying in the Qt libraries and plugins that";
qInfo() << "the application uses.";
qInfo() << "";
qInfo() << "By default it deploys the Qt instance that qmake on the $PATH points to.";
qInfo() << "The '-qmake' option can be used to point to the qmake executable";
qInfo() << "to be used instead.";
qInfo() << "";
qInfo() << "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.";
qInfo() << "";
qInfo() << "See the \"Deploying Applications on Linux\" topic in the";
qInfo() << "documentation for more information about deployment on Linux.";
return 1;
}
@@ -88,6 +122,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();
@@ -167,13 +207,16 @@ int main(int argc, char **argv)
extern bool bundleAllButCoreLibs;
extern bool fhsLikeMode;
extern QString fhsPrefix;
extern bool alwaysOwerwriteEnabled;
extern QStringList librarySearchPath;
extern bool alwaysOwerwriteEnabled;
QStringList additionalExecutables;
bool qmldirArgumentUsed = false;
bool skipTranslations = false;
QStringList qmlDirs;
QString qmakeExecutable;
extern QStringList extraQtPlugins;
extern QStringList excludeLibs;
extern bool copyCopyrightFiles;
/* 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. */
@@ -299,7 +342,7 @@ int main(int argc, char **argv)
}
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 + "/.DirIcon").exists() == false) QFile::copy(preExistingToplevelIcon, appDirPath + "/.DirIcon");
}
if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){
preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg";
@@ -326,8 +369,10 @@ int main(int argc, char **argv)
}
}
// Check arguments
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
if (argument == QByteArray("-no-plugins")) {
LogDebug() << "Argument found:" << argument;
plugins = false;
@@ -365,6 +410,9 @@ int main(int argc, char **argv)
LogError() << "Missing qml directory path";
else
qmlDirs << argument.mid(index+1);
} else if (argument.startsWith("-no-copy-copyright-files")) {
LogDebug() << "Argument found:" << argument;
copyCopyrightFiles = false;
} else if (argument == QByteArray("-always-overwrite")) {
LogDebug() << "Argument found:" << argument;
alwaysOwerwriteEnabled = true;
@@ -375,11 +423,22 @@ int main(int argc, char **argv)
} else if (argument == QByteArray("-no-translations")) {
LogDebug() << "Argument found:" << argument;
skipTranslations = true;
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
} else if (argument.startsWith("-extra-plugins=")) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
extraQtPlugins = QString(argument.mid(index + 1)).split(",");
} else if (argument.startsWith("-exclude-libs=")) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
excludeLibs = QString(argument.mid(index + 1)).split(",");
} else if (argument.startsWith("--")) {
LogError() << "Error: arguments must not start with --, only -:" << argument << "\n";
return 1;
} else {
LogError() << "Unknown argument:" << argument << "\n";
return 1;
}
}
}
if (appimage) {
if(checkAppImagePrerequisites(appDirPath) == false){
@@ -388,6 +447,11 @@ int main(int argc, char **argv)
}
}
if (!excludeLibs.isEmpty())
{
qWarning() << "WARNING: Excluding the following libraries might break the AppImage. Please double-check the list:" << excludeLibs;
}
DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables,
qmakeExecutable);
+227 -76
View File
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd. and Simon Peter
** Copyright (C) 2016-18 The Qt Company Ltd. and Simon Peter
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -44,6 +44,7 @@
#include <QRegularExpression>
#include <QStandardPaths>
#include "shared.h"
#include "excludelist.h"
QString appBinaryPath;
bool runStripEnabled = true;
@@ -57,6 +58,9 @@ int logLevel = 1;
int qtDetected = 0;
bool qtDetectionComplete = 0; // As long as Qt is not detected yet, ldd may encounter "not found" messages, continue anyway
bool deployLibrary = false;
QStringList extraQtPlugins;
QStringList excludeLibs;
bool copyCopyrightFiles = true;
using std::cout;
using std::endl;
@@ -212,38 +216,22 @@ inline QDebug operator<<(QDebug debug, const AppDirInfo &info)
return debug;
}
// Determine whether the given 'ldd' output contains a Linux VDSO
// shared object. The name of the VDSO object differs depending
// 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;
}
bool copyFilePrintStatus(const QString &from, const QString &to)
{
if (QFile(to).exists()) {
if (alwaysOwerwriteEnabled) {
QFile(to).remove();
} else {
LogDebug() << QFileInfo(to).fileName() << "already deployed, skipping.";
return false;
LogDebug() << QFileInfo(to).fileName() << "already exists at target location";
return true;
}
}
QDir dir(QDir::cleanPath(to + "/../"));
if (!dir.exists()) {
dir.mkpath(".");
}
if (QFile::copy(from, to)) {
QFile dest(to);
dest.setPermissions(dest.permissions() | QFile::WriteOwner | QFile::WriteUser);
@@ -270,6 +258,81 @@ bool copyFilePrintStatus(const QString &from, const QString &to)
}
}
bool copyCopyrightFile(QString libPath){
/* When deploying files (e.g., libraries) from the
* system, then try to also deploy their copyright file.
* This is currently only implemented for dpkg-based,
* Debian-like systems. Pull requests welcome for other
* systems. */
if (!copyCopyrightFiles) {
LogNormal() << "Skipping copyright files deployment as requested by the user";
return false;
}
QString dpkgPath;
dpkgPath = QStandardPaths::findExecutable("dpkg");
if(dpkgPath == ""){
LogNormal() << "dpkg not found, hence not deploying copyright files";
return false;
}
QString dpkgQueryPath;
dpkgQueryPath = QStandardPaths::findExecutable("dpkg-query");
if(dpkgQueryPath == ""){
LogNormal() << "dpkg-query not found, hence not deploying copyright files";
return false;
}
QString copyrightFilePath;
/* Find out which package the file being deployed belongs to */
QStringList arguments;
arguments << "-S" << libPath;
QProcess *myProcess = new QProcess();
myProcess->start(dpkgPath, arguments);
myProcess->waitForFinished();
QString strOut = myProcess->readAllStandardOutput().split(':')[0];
if(strOut == "") return false;
/* Find out the copyright file in that package */
arguments << "-L" << strOut;
myProcess->start(dpkgQueryPath, arguments);
myProcess->waitForFinished();
strOut = myProcess->readAllStandardOutput();
QStringList outputLines = strOut.split("\n", QString::SkipEmptyParts);
foreach (QString outputLine, outputLines) {
if((outputLine.contains("usr/share/doc")) && (outputLine.contains("/copyright")) && (outputLine.contains(" "))){
// copyrightFilePath = outputLine.split(' ')[1]; // This is not working on multiarch systems; see https://github.com/probonopd/linuxdeployqt/issues/184#issuecomment-345293540
QStringList parts = outputLine.split(' ');
copyrightFilePath = parts[parts.size() - 1]; // Grab last element
break;
}
}
if(copyrightFilePath == "") return false;
LogDebug() << "copyrightFilePath:" << copyrightFilePath;
/* Where should we copy this file to? We are assuming the Debian-like path contains
* the name of the package like so: copyrightFilePath: "/usr/share/doc/libpcre3/copyright"
* this assumption is most likely only true for Debian-like systems */
QString packageName = copyrightFilePath.split("/")[copyrightFilePath.split("/").length()-2];
QString copyrightFileTargetPath;
if(fhsLikeMode){
copyrightFileTargetPath = QDir::cleanPath(appBinaryPath + "/../../share/doc/" + packageName + "/copyright");
} else {
copyrightFileTargetPath = QDir::cleanPath(appBinaryPath + "/../doc/" + packageName + "/copyright");
}
/* Do the actual copying */
return(copyFilePrintStatus(copyrightFilePath, copyrightFileTargetPath));
}
LddInfo findDependencyInfo(const QString &binaryPath)
{
LddInfo info;
@@ -309,20 +372,19 @@ LddInfo findDependencyInfo(const QString &binaryPath)
// LogDebug() << "ldd outputLine:" << outputLine;
if ((outputLine.contains("not found")) && (qtDetectionComplete == 1)){
LogError() << "ldd outputLine:" << outputLine.replace("\t", "");
LogError() << "for binary:" << binaryPath;
LogError() << "Please ensure that all libraries can be found by ldd. Aborting.";
exit(1);
}
}
if ((binaryPath.contains(".so.") || binaryPath.endsWith(".so")) && (!lddOutputContainsLinuxVDSO(output))) {
const QRegularExpressionMatch match = regexp.match(outputLines.first());
if (match.hasMatch()) {
info.installName = match.captured(1);
} else {
LogError() << "Could not parse ldd output line:" << outputLines.first();
}
outputLines.removeFirst();
/*
FIXME: For unknown reasons, this segfaults; see https://travis-ci.org/probonopd/Labrador/builds/339803886#L1320
if (binaryPath.contains("platformthemes")) {
LogDebug() << "Not adding dependencies of" << binaryPath << "because we do not bundle dependencies of platformthemes";
return info;
}
*/
foreach (const QString &outputLine, outputLines) {
const QRegularExpressionMatch match = regexp.match(outputLine);
@@ -373,16 +435,15 @@ LibraryInfo parseLddLibraryLine(const QString &line, const QString &appDirPath,
This is more suitable for bundling in a way that is portable between different distributions and target systems.
Along the way, this also takes care of non-Qt libraries.
The excludelist can be updated by running
#/bin/bash
blacklisted=$(wget https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | grep -v "^#.*" | grep "[^-\s]")
for item in $blacklisted; do
echo -ne '"'$item'" << '
done
The excludelist can be updated by running the bundled script generate-excludelist.sh
*/
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" << "libSM.so.6" << "libstdc++.so.6" << "libusb-1.0.so.0" << "libuuid.so.1" << "libX11.so.6" << "libxcb.so.1" << "libz.so.1";
// copy generated excludelist
QStringList excludelist = generatedExcludelist;
// append exclude libs
excludelist += excludeLibs;
LogDebug() << "excludelist:" << excludelist;
if (! trimmed.contains("libicu")) {
if (containsHowOften(excludelist, QFileInfo(trimmed).completeBaseName())) {
@@ -575,10 +636,13 @@ QList<LibraryInfo> getQtLibrariesForPaths(const QStringList &paths, const QStrin
QSet<QString> existing;
foreach (const QString &path, paths) {
foreach (const LibraryInfo &info, getQtLibraries(path, appDirPath, rpaths)) {
if (!existing.contains(info.libraryPath)) { // avoid duplicates
existing.insert(info.libraryPath);
result << info;
if (!excludeLibs.contains(QFileInfo(path).baseName()))
{
foreach (const LibraryInfo &info, getQtLibraries(path, appDirPath, rpaths)) {
if (!existing.contains(info.libraryPath)) { // avoid duplicates
existing.insert(info.libraryPath);
result << info;
}
}
}
}
@@ -599,6 +663,8 @@ bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
const QString fileSourcePath = sourcePath + "/" + file;
const QString fileDestinationPath = destinationPath + "/" + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
LogDebug() << "copyCopyrightFile:" << fileSourcePath;
copyCopyrightFile(fileSourcePath);
}
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
@@ -620,6 +686,8 @@ void recursiveCopyAndDeploy(const QString &appDirPath, const QSet<QString> &rpat
QString fileDestinationPath = destinationPath + QLatin1Char('/') + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
LogDebug() << "copyCopyrightFile:" << fileSourcePath;
copyCopyrightFile(fileSourcePath);
if(fileDestinationPath.endsWith(".so")){
@@ -675,6 +743,8 @@ QString copyDylib(const LibraryInfo &library, const QString path)
// Copy dylib binary
copyFilePrintStatus(library.sourceFilePath, dylibDestinationBinaryPath);
LogDebug() << "copyCopyrightFile:" << library.sourceFilePath;
copyCopyrightFile(library.sourceFilePath);
return dylibDestinationBinaryPath;
}
@@ -920,13 +990,12 @@ DeploymentInfo deployQtLibraries(QList<LibraryInfo> libraries,
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;
}
if (library.libraryDirectory.startsWith(bundlePath)) {
LogNormal() << library.libraryName << "already deployed, skipping.";
continue;
LogNormal() << library.libraryName << "already at target location";
}
if (library.rpathUsed.isEmpty() != true) {
@@ -1071,9 +1140,16 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
} else {
libraryPath = QFileInfo(applicationBundle.binaryPath).dir().filePath("../lib/" + bundleLibraryDirectory);
}
foreach (const QString &executable, QStringList() << applicationBundle.binaryPath << additionalExecutables) {
changeIdentification("$ORIGIN/" + QFileInfo(executable).dir().relativeFilePath(libraryPath) + "/" + bundleLibraryDirectory, QFileInfo(executable).canonicalFilePath());
}
/* Make ldd detect pre-existing libraries in the AppDir.
* TODO: Consider searching the AppDir for .so* files outside of libraryPath
* and warning about them not being taken into consideration */
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString oldPath = env.value("LD_LIBRARY_PATH");
QString newPath = libraryPath + ":" + oldPath; // FIXME: If we use a ldd replacement, we still need to observe this path
LogDebug() << "Changed LD_LIBRARY_PATH:" << newPath;
setenv("LD_LIBRARY_PATH",newPath.toUtf8().constData(),1);
applicationBundle.libraryPaths = findAppLibraries(appDirPath);
LogDebug() << "applicationBundle.libraryPaths:" << applicationBundle.libraryPaths;
@@ -1088,17 +1164,24 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
LogDebug() << "allRPaths:" << allRPaths;
QList<LibraryInfo> libraries = getQtLibrariesForPaths(allBinaryPaths, appDirPath, allRPaths);
DeploymentInfo depInfo;
if (libraries.isEmpty() && !alwaysOwerwriteEnabled) {
LogWarning() << "Could not find any external Qt libraries to deploy in" << appDirPath;
LogWarning() << "Perhaps linuxdeployqt was already used on" << appDirPath << "?";
LogWarning() << "If so, you will need to rebuild" << appDirPath << "before trying again.";
LogWarning() << "Or ldd does not find the external Qt libraries but sees the system ones.";
LogWarning() << "If so, you will need to set LD_LIBRARY_PATH to the directory containing the external Qt libraries before trying again.";
LogWarning() << "FIXME: https://github.com/probonopd/linuxdeployqt/issues/2";
return DeploymentInfo();
LogWarning() << "Could not find any external Qt libraries to deploy in" << appDirPath;
LogWarning() << "Perhaps linuxdeployqt was already used on" << appDirPath << "?";
LogWarning() << "If so, you will need to rebuild" << appDirPath << "before trying again.";
LogWarning() << "Or ldd does not find the external Qt libraries but sees the system ones.";
LogWarning() << "If so, you will need to set LD_LIBRARY_PATH to the directory containing the external Qt libraries before trying again.";
LogWarning() << "FIXME: https://github.com/probonopd/linuxdeployqt/issues/2";
} else {
return deployQtLibraries(libraries, applicationBundle.path, allBinaryPaths, !additionalExecutables.isEmpty());
depInfo = deployQtLibraries(libraries, applicationBundle.path, allBinaryPaths, !additionalExecutables.isEmpty());
}
foreach (const QString &executable, QStringList() << applicationBundle.binaryPath << additionalExecutables) {
changeIdentification("$ORIGIN/" + QFileInfo(executable).dir().relativeFilePath(libraryPath), QFileInfo(executable).canonicalFilePath());
}
return depInfo;
}
void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath,
@@ -1118,6 +1201,20 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Gui")) {
LogDebug() << "libQt5Gui detected";
pluginList.append("platforms/libqxcb.so");
// Platform plugin contexts - apparently needed to enter special characters
QStringList platformPluginContexts = QDir(pluginSourcePath + QStringLiteral("/platforminputcontexts")).entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &plugin, platformPluginContexts) {
pluginList.append(QStringLiteral("platforminputcontexts/") + plugin);
}
// Platform themes - make Qt look more native e.g., on Gtk+ 3 (if available in Qt installation)
// FIXME: Do not do this until we find a good way to do this without also deploying their dependencies
// See https://github.com/probonopd/linuxdeployqt/issues/236
/*
QStringList platformThemes = QDir(pluginSourcePath + QStringLiteral("/platformthemes")).entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &plugin, platformThemes) {
pluginList.append(QStringLiteral("platformthemes/") + plugin);
}
*/
// All image formats (svg if QtSvg library is used)
QStringList imagePlugins = QDir(pluginSourcePath + QStringLiteral("/imageformats")).entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &plugin, imagePlugins) {
@@ -1132,7 +1229,9 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
}
// Platform OpenGL context
if ((containsHowOften(deploymentInfo.deployedLibraries, "libQt5OpenGL")) or (containsHowOften(deploymentInfo.deployedLibraries, "libQt5XcbQpa"))) {
if ((containsHowOften(deploymentInfo.deployedLibraries, "libQt5OpenGL"))
or (containsHowOften(deploymentInfo.deployedLibraries, "libQt5XcbQpa"))
or (containsHowOften(deploymentInfo.deployedLibraries, "libxcb-glx"))) {
QStringList xcbglintegrationPlugins = QDir(pluginSourcePath + QStringLiteral("/xcbglintegrations")).entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &plugin, xcbglintegrationPlugins) {
pluginList.append(QStringLiteral("xcbglintegrations/") + plugin);
@@ -1166,6 +1265,14 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
}
}
// Positioning plugins if QtPositioning library is in use
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Positioning")) {
QStringList posPlugins = QDir(pluginSourcePath + QStringLiteral("/position")).entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &plugin, posPlugins) {
pluginList.append(QStringLiteral("position/") + plugin);
}
}
// multimedia plugins if QtMultimedia library is in use
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Multimedia")) {
QStringList plugins = QDir(pluginSourcePath + QStringLiteral("/mediaservice")).entryList(QStringList() << QStringLiteral("*.so"));
@@ -1238,25 +1345,60 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
recursiveCopy(sourcePath, destinationPath);
}
if (!extraQtPlugins.isEmpty()) {
LogNormal() << "Deploying extra plugins.";
foreach (const QString &plugin, extraQtPlugins) {
QDir pluginDirectory(pluginSourcePath + "/" + plugin);
if (pluginDirectory.exists()) {
//If it is a plugin directory we will deploy the entire directory
QStringList plugins = pluginDirectory.entryList(QStringList() << QStringLiteral("*.so"));
foreach (const QString &pluginFile, plugins) {
pluginList.append(plugin + "/" + pluginFile);
LogDebug() << plugin + "/" + pluginFile << "appended";
}
}
else {
//If it isn't a directory we asume it is an explicit plugin and we will try to deploy that
if (!pluginList.contains(plugin)) {
if (QFile::exists(pluginSourcePath + "/" + plugin)) {
pluginList.append(plugin);
LogDebug() << plugin << "appended";
}
else {
LogDebug() << "The plugin" << plugin << "was already deployed." ;
}
}
else {
LogWarning() << "The plugin" << pluginSourcePath + "/" + plugin << "could not be found. Please check spelling and try again!";
}
}
}
}
LogNormal() << "pluginList after having detected hopefully all required plugins:" << pluginList;
foreach (const QString &plugin, pluginList) {
sourcePath = pluginSourcePath + "/" + plugin;
destinationPath = pluginDestinationPath + "/" + plugin;
QDir dir;
dir.mkpath(QFileInfo(destinationPath).path());
QList<LibraryInfo> libraries = getQtLibraries(sourcePath, appDirInfo.path, deploymentInfo.rpathsUsed);
LogDebug() << "Deploying plugin" << sourcePath;
if (copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
deployQtLibraries(libraries, appDirInfo.path, QStringList() << destinationPath, deploymentInfo.useLoaderPath);
/* See whether this makes any difference */
// Find out the relative path to the lib/ directory and set it as the rpath
QDir dir(destinationPath);
QString relativePath = dir.relativeFilePath(appDirInfo.path + "/" + libraries[0].libraryDestinationDirectory);
relativePath.remove(0, 3); // remove initial '../'
changeIdentification("$ORIGIN/" + relativePath, QFileInfo(destinationPath).canonicalFilePath());
if(!excludeLibs.contains(QFileInfo(sourcePath).baseName()))
{
QDir dir;
dir.mkpath(QFileInfo(destinationPath).path());
QList<LibraryInfo> libraries = getQtLibraries(sourcePath, appDirInfo.path, deploymentInfo.rpathsUsed);
LogDebug() << "Deploying plugin" << sourcePath;
if (copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
deployQtLibraries(libraries, appDirInfo.path, QStringList() << destinationPath, deploymentInfo.useLoaderPath);
/* See whether this makes any difference */
// Find out the relative path to the lib/ directory and set it as the rpath
QDir dir(destinationPath);
QString relativePath = dir.relativeFilePath(appDirInfo.path + "/" + libraries[0].libraryDestinationDirectory);
relativePath.remove(0, 3); // remove initial '../'
changeIdentification("$ORIGIN/" + relativePath, QFileInfo(destinationPath).canonicalFilePath());
}
LogDebug() << "copyCopyrightFile:" << sourcePath;
copyCopyrightFile(sourcePath);
}
}
}
@@ -1571,7 +1713,7 @@ bool checkAppImagePrerequisites(const QString &appDirPath)
int createAppImage(const QString &appDirPath)
{
QString appImageCommand = "appimagetool '" + appDirPath + "' --verbose -n"; // +"' '" + appImagePath + "'";
QString appImageCommand = "appimagetool '" + appDirPath + "' --verbose -n -g"; // +"' '" + appImagePath + "'";
int ret = system(appImageCommand.toUtf8().constData());
LogNormal() << "ret" << ret;
LogNormal() << "WEXITSTATUS(ret)" << WEXITSTATUS(ret);
@@ -1613,9 +1755,18 @@ void deployTranslations(const QString &appDirPath, quint64 usedQtModules)
return;
}
QString translationsDirPath = appDirPath + QStringLiteral("/translations");
LogDebug() << "Using" << translationsDirPath << "as translations directory for App";
LogDebug() << "Using" << qtTranslationsPath << " to search for Qt translations";
QString translationsDirPath;
if (!fhsLikeMode) {
translationsDirPath = appDirPath + QStringLiteral("/translations");
} else {
// TODO: refactor this global variables hack
QFileInfo appBinaryFI(appBinaryPath);
QString appRoot = appBinaryFI.absoluteDir().absolutePath() + "/../";
translationsDirPath = appRoot + QStringLiteral("/translations");
}
LogNormal() << "Using" << translationsDirPath << "as translations directory for App";
LogNormal() << "Using" << qtTranslationsPath << " to search for Qt translations";
QFileInfo fi(translationsDirPath);
if (!fi.isDir()) {
+3 -1
View File
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd. and Simon Peter
** Copyright (C) 2016-18 The Qt Company Ltd. and Simon Peter
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -45,6 +45,8 @@ extern bool runStripEnabled;
extern bool bundleAllButCoreLibs;
extern bool fhsLikeMode;
extern QString fhsPrefix;
extern QStringList extraQtPlugins;
extern QStringList excludeLibs;
class LibraryInfo
{