Merge branch 'master' into find_qmake_in_exepath
This commit is contained in:
@@ -36,3 +36,4 @@ Makefile*
|
|||||||
# QtCtreator CMake
|
# QtCtreator CMake
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
|
|
||||||
|
.idea
|
||||||
|
|||||||
+7
-1
@@ -11,12 +11,18 @@ env:
|
|||||||
before_install:
|
before_install:
|
||||||
- ./tests/tests-environment.sh
|
- ./tests/tests-environment.sh
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
# fetch all tags
|
||||||
|
- git fetch --unshallow
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./tests/tests-ci.sh
|
- ./tests/tests-ci.sh
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
|
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
|
||||||
- bash ./upload.sh ./linuxdeployqt-*.AppImage
|
# quick fix for issue 223
|
||||||
|
- if [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi
|
||||||
|
- bash ./upload.sh ./linuxdeployqt-*.AppImage*
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- "xpra stop :99"
|
- "xpra stop :99"
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# CMake configuration for linuxdeployqt
|
||||||
|
# Not meant to replace the qmake build system, but for use with CMake based IDEs.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(linuxdeployqt)
|
||||||
|
|
||||||
|
find_program(GIT git)
|
||||||
|
|
||||||
|
# make sure Git revision ID and latest tag is not stored in the CMake cache
|
||||||
|
# otherwise, one would have to reset the CMake cache on every new commit to make sure the Git commit ID is up to date
|
||||||
|
unset(GIT_COMMIT CACHE)
|
||||||
|
unset(GIT_LATEST_TAG CACHE)
|
||||||
|
|
||||||
|
if("${GIT}" STREQUAL "GIT-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Could not find git")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# read Git revision ID and latest tag number
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT}" rev-parse --short HEAD
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE GIT_COMMIT_RESULT
|
||||||
|
)
|
||||||
|
if(NOT GIT_COMMIT_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to determine git commit ID")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(GIT_COMMIT GIT_COMMIT_RESULT)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT}" rev-list --tags --skip=1 --max-count=1
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_TAG_ID
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE GIT_TAG_ID_RESULT
|
||||||
|
)
|
||||||
|
if(NOT GIT_TAG_ID_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to determine git tag ID")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(GIT_TAG_ID GIT_TAG_ID_RESULT)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT}" describe --tags ${GIT_TAG_ID} --abbrev=0
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_TAG_NAME
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE GIT_TAG_NAME_RESULT
|
||||||
|
)
|
||||||
|
if(NOT GIT_TAG_NAME_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to determine git tag name")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(GIT_TAG_NAME GIT_TAG_NAME_RESULT)
|
||||||
|
|
||||||
|
# set version and build number
|
||||||
|
set(VERSION 1-alpha)
|
||||||
|
if("$ENV{TRAVIS_BUILD_NUMBER}" STREQUAL "")
|
||||||
|
set(BUILD_NUMBER "<local dev build>")
|
||||||
|
else()
|
||||||
|
set(BUILD_NUMBER "$ENV{TRAVIS_BUILD_NUMBER}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# get current date
|
||||||
|
execute_process(
|
||||||
|
COMMAND env LC_ALL=C date -u "+%Y-%m-%d %H:%M:%S %Z"
|
||||||
|
OUTPUT_VARIABLE DATE
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE DATE_RESULT
|
||||||
|
)
|
||||||
|
if(NOT DATE_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to determine date string")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(DATE DATE_RESULT)
|
||||||
|
|
||||||
|
add_subdirectory(tools/linuxdeployqt)
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd.
|
The Qt Toolkit is Copyright (C) 2016-18 The Qt Company Ltd.
|
||||||
Contact: http://www.qt.io/licensing/
|
Contact: http://www.qt.io/licensing/
|
||||||
|
|
||||||
You may use, distribute and copy the Qt Toolkit under the terms of
|
You may use, distribute and copy the Qt Toolkit under the terms of
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd.
|
The Qt Toolkit is Copyright (C) 2016-18 The Qt Company Ltd.
|
||||||
Contact: http://www.qt.io/licensing/
|
Contact: http://www.qt.io/licensing/
|
||||||
|
|
||||||
You may use, distribute and copy the Qt Toolkit under the terms of
|
You may use, distribute and copy the Qt Toolkit under the terms of
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# linuxdeployqt [](https://travis-ci.org/probonopd/linuxdeployqt) [](https://www.codacy.com/app/probonopd/linuxdeployqt?utm_source=github.com&utm_medium=referral&utm_content=probonopd/linuxdeployqt&utm_campaign=Badge_Grade) [](http://discourse.appimage.org/t/linuxdeployqt-new-linux-deployment-tool-for-qt/57) [](https://gitter.im/probonopd/AppImageKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://webchat.freenode.net/?channels=AppImage)
|
# linuxdeployqt [](https://travis-ci.org/probonopd/linuxdeployqt) [](https://www.codacy.com/app/probonopd/linuxdeployqt?utm_source=github.com&utm_medium=referral&utm_content=probonopd/linuxdeployqt&utm_campaign=Badge_Grade) [](http://discourse.appimage.org/t/linuxdeployqt-new-linux-deployment-tool-for-qt/57) [](https://gitter.im/probonopd/AppImageKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://webchat.freenode.net/?channels=AppImage)
|
||||||
|
|
||||||
This Linux Deployment Tool for Qt, `linuxdeployqt`, takes an application as input and makes it self-contained by copying in the Qt libraries and plugins that the application uses into a bundle. This can optionally be put into an [AppImage](http://appimage.org/), and, using [fpm](https://github.com/probonopd/linuxdeployqt/issues/9), into cross-distro deb and rpm packages.
|
This Linux Deployment Tool, `linuxdeployqt`, takes an application as input and makes it self-contained by copying in the resources that the application uses (like libraries, graphics, and plugins) into a bundle. The resulting bundle can be distributed as an AppDir or as an [AppImage](https://appimage.org/) to users, or can be put into cross-distribution packages. It can be used as part of the build process to deploy applications written in C, C++, and other compiled languages with systems like `CMake`, `qmake`, and `make`. When used on Qt-based applications, it can bundle a specific minimal subset of Qt required to run the application.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Differences to macdeployqt
|
## Differences to macdeployqt
|
||||||
This tool is conceptually based on the [Mac Deployment Tool](http://doc.qt.io/qt-5/osx-deployment.html), `macdeployqt` in the tools applications of the Qt Toolkit, but has been changed to a slightly different logic and other tools needed for Linux.
|
This tool is conceptually based on the [Mac Deployment Tool](http://doc.qt.io/qt-5/osx-deployment.html), `macdeployqt` in the tools applications of the Qt Toolkit, but has been changed to a slightly different logic and other tools needed for Linux.
|
||||||
@@ -32,6 +34,7 @@ Options:
|
|||||||
-always-overwrite : Copy files even if the target file exists
|
-always-overwrite : Copy files even if the target file exists
|
||||||
-qmake=<path> : The qmake executable to use
|
-qmake=<path> : The qmake executable to use
|
||||||
-no-translations : Skip deployment of translations
|
-no-translations : Skip deployment of translations
|
||||||
|
-extra-plugins=<list> : List of extra plugins which should be deployed, separated by comma
|
||||||
|
|
||||||
linuxdeployqt takes an application as input and makes it
|
linuxdeployqt takes an application as input and makes it
|
||||||
self-contained by copying in the Qt libraries and plugins that
|
self-contained by copying in the Qt libraries and plugins that
|
||||||
@@ -40,12 +43,43 @@ the application uses.
|
|||||||
|
|
||||||
#### Simplest example
|
#### Simplest example
|
||||||
|
|
||||||
Given that a desktop file should be provided with an AppImage, `linuxdeployqt` can use that to determine the parameters of the build.
|
You'll need to provide the basic structure of an `AppDir` which should look something like this:
|
||||||
|
```
|
||||||
|
└── usr
|
||||||
|
├── bin
|
||||||
|
│ └── your_app
|
||||||
|
├── lib
|
||||||
|
└── share
|
||||||
|
├── applications
|
||||||
|
│ └── your_app.desktop
|
||||||
|
└── icons
|
||||||
|
└── <theme>
|
||||||
|
└── <resolution>
|
||||||
|
└── apps
|
||||||
|
└── your_app.png
|
||||||
|
```
|
||||||
|
Replace `<theme>` and `<resolution>` with (for example) `hicolor` and `256x256` respectively; see [icon theme spec](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html) for more details.
|
||||||
|
|
||||||
`linuxdeployqt ./path/to/appdir/usr/share/application_name.desktop`
|
|
||||||
|
|
||||||
Where the _desktop_ file specifies the executable to be run (with `EXEC=`), the name of the applications and an icon.
|
Using the desktop file `linuxdeployqt` can determine the parameters of the build.
|
||||||
See [desktop file specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html).
|
|
||||||
|
Where your desktop file would look something like:
|
||||||
|
```
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Amazing Qt App
|
||||||
|
Comment=The best Qt Application Ever
|
||||||
|
Exec=your_app
|
||||||
|
Icon=your_app
|
||||||
|
Categories=Office;
|
||||||
|
```
|
||||||
|
|
||||||
|
* Notice that both `Exec` and `Icon` only have file names.
|
||||||
|
* Also Notice that the `Icon` entry does not include an extension.
|
||||||
|
|
||||||
|
Read more about desktop files in the [freedesktop specification here](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html).
|
||||||
|
|
||||||
|
Now you can say: `linuxdeployqt-continuous-x86_64.AppImage path/to/AppDir/usr/share/applications/your_app.desktop`
|
||||||
|
|
||||||
For a more detailed example, see "Using linuxdeployqt with Travis CI" below.
|
For a more detailed example, see "Using linuxdeployqt with Travis CI" below.
|
||||||
|
|
||||||
@@ -78,6 +112,18 @@ find $HOME/build-*-*_Qt_* \( -name "moc_*" -or -name "*.o" -or -name "qrc_*" -or
|
|||||||
|
|
||||||
Alternatively, you could use `$DESTDIR`.
|
Alternatively, you could use `$DESTDIR`.
|
||||||
|
|
||||||
|
#### Adding extra Qt plugins
|
||||||
|
|
||||||
|
If you want aditional plugins which the tool doesn't deploy, for a variety of reasons, you can use the -extra-plugins argument and include a list of plugins separated by a comma.
|
||||||
|
The plugins deployed are from the Qt installation pointed out by `qmake -v`.
|
||||||
|
You can deploy entire plugin directories, a specific directory or a mix of both.
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
|
||||||
|
1. `-extra-plugins=sqldrivers/libqmsql.so,iconengines/libqsvgicon.so`
|
||||||
|
2. `-extra-plugins=sqldrivers,iconengines/libqsvgicon.so`
|
||||||
|
3. `-extra-plugins=sqldrivers,iconengines,mediaservice,gamepads`
|
||||||
|
|
||||||
## Using linuxdeployqt with Travis CI
|
## Using linuxdeployqt with Travis CI
|
||||||
|
|
||||||
A common use case for `linuxdeployqt` is to use it on Travis CI after the `make` command. The following example illustrates how to use `linuxdeployqt` with Travis CI. Create a `.travis.yml` file similar to this one (be sure to customize it, e.g., change `APPNAME` to the name of your application as it is spelled in the `Name=` entry of the `.desktop` file):
|
A common use case for `linuxdeployqt` is to use it on Travis CI after the `make` command. The following example illustrates how to use `linuxdeployqt` with Travis CI. Create a `.travis.yml` file similar to this one (be sure to customize it, e.g., change `APPNAME` to the name of your application as it is spelled in the `Name=` entry of the `.desktop` file):
|
||||||
@@ -89,7 +135,7 @@ sudo: require
|
|||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- sudo add-apt-repository ppa:beineri/opt-qt59-trusty -y
|
- sudo add-apt-repository ppa:beineri/opt-qt593-trusty -y
|
||||||
- sudo apt-get update -qq
|
- sudo apt-get update -qq
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@@ -100,15 +146,23 @@ script:
|
|||||||
- qmake CONFIG+=release PREFIX=/usr
|
- qmake CONFIG+=release PREFIX=/usr
|
||||||
- make -j$(nproc)
|
- make -j$(nproc)
|
||||||
- make INSTALL_ROOT=appdir -j$(nproc) install ; find appdir/
|
- make INSTALL_ROOT=appdir -j$(nproc) install ; find appdir/
|
||||||
- wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
- wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||||
- chmod a+x linuxdeployqt*.AppImage
|
- chmod a+x linuxdeployqt-continuous-x86_64.AppImage
|
||||||
- unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
- unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
||||||
- ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
|
- export VERSION=$(git rev-parse --short HEAD) # linuxdeployqt uses this for naming the file
|
||||||
- ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage
|
- ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
|
||||||
|
- ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -appimage
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
|
- 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
|
- # curl --upload-file APPNAME*.AppImage https://transfer.sh/APPNAME-git.$(git rev-parse --short HEAD)-x86_64.AppImage
|
||||||
|
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
|
||||||
|
- bash upload.sh APPNAME*.AppImage*
|
||||||
|
|
||||||
|
branches:
|
||||||
|
except:
|
||||||
|
- # Do not build tags that we create when we upload to GitHub Releases
|
||||||
|
- /^(?i:continuous)/
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
@@ -124,6 +178,10 @@ find: `appdir/': No such file or directory
|
|||||||
|
|
||||||
If `qmake` does not allow for `make install` or does not install the desktop file and icon, then you need to change your `.pro` file it similar to https://github.com/probonopd/FeedTheMonkey/blob/master/FeedTheMonkey.pro.
|
If `qmake` does not allow for `make install` or does not install the desktop file and icon, then you need to change your `.pro` file it similar to https://github.com/probonopd/FeedTheMonkey/blob/master/FeedTheMonkey.pro.
|
||||||
|
|
||||||
|
It is common on Unix to also use the build tool to install applications and libraries; for example, by invoking `make install`. For this reason, `qmake` has the concept of an install set, an object which contains instructions about the way a part of a project is to be installed.
|
||||||
|
|
||||||
|
Please see the section "Installing Files" on http://doc.qt.io/qt-5/qmake-advanced-usage.html.
|
||||||
|
|
||||||
```
|
```
|
||||||
- make INSTALL_ROOT=appdir install ; find appdir/
|
- make INSTALL_ROOT=appdir install ; find appdir/
|
||||||
```
|
```
|
||||||
@@ -159,9 +217,7 @@ The exception is that you are building Qt libraries that _should_ be installed t
|
|||||||
`linuxdeployqt` is great for upstream application projects that want to release their software in binary form to Linux users quickly and without much overhead. If you would like to see a particular application use `linuxdeployqt`, then sending a Pull Request may be an option to get the upstream application project to consider it. You can use the following template text for Pull Requests but make sure to customize it to the project in question.
|
`linuxdeployqt` is great for upstream application projects that want to release their software in binary form to Linux users quickly and without much overhead. If you would like to see a particular application use `linuxdeployqt`, then sending a Pull Request may be an option to get the upstream application project to consider it. You can use the following template text for Pull Requests but make sure to customize it to the project in question.
|
||||||
|
|
||||||
```
|
```
|
||||||
This PR, when merged, will compile this application on [Travis CI](https://travis-ci.org/) upon each `git push`, and upload an [AppImage](http://appimage.org/) to a temporary download URL on transfer.sh (available for 14 days). The download URL is toward the end of each Travis CI build log of each build (see below for how to set up automatic uploading to your GitHub Releases page).
|
This PR, when merged, will compile this application on [Travis CI](https://travis-ci.org/) upon each `git push`, and upload an [AppImage](http://appimage.org/) to your GitHub Releases page.
|
||||||
|
|
||||||
For this to work, you need to enable Travis CI for your repository as [described here](https://travis-ci.org/getting_started) __prior to merging this__, if you haven't already done so.
|
|
||||||
|
|
||||||
Providing an [AppImage](http://appimage.org/) would have, among others, these advantages:
|
Providing an [AppImage](http://appimage.org/) would have, among others, these advantages:
|
||||||
- Applications packaged as an AppImage can run on many distributions (including Ubuntu, Fedora, openSUSE, CentOS, elementaryOS, Linux Mint, and others)
|
- Applications packaged as an AppImage can run on many distributions (including Ubuntu, Fedora, openSUSE, CentOS, elementaryOS, Linux Mint, and others)
|
||||||
@@ -175,12 +231,12 @@ Providing an [AppImage](http://appimage.org/) would have, among others, these ad
|
|||||||
- Can optionally GPG2-sign your AppImages (inside the file)
|
- Can optionally GPG2-sign your AppImages (inside the file)
|
||||||
- Works on Live ISOs
|
- Works on Live ISOs
|
||||||
- Can use the same AppImages when dual-booting multiple distributions
|
- 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
|
- Can be listed in the [AppImageHub](https://appimage.github.io/apps) central directory of available AppImages
|
||||||
|
- Can double as a self-extracting compressed archive with the `--appimage-extract` parameter
|
||||||
|
|
||||||
[Here is an overview](https://github.com/probonopd/AppImageKit/wiki/AppImages) of projects that are already distributing upstream-provided, official AppImages.
|
[Here is an overview](https://appimage.github.io/apps) of projects that are already distributing upstream-provided, official AppImages.
|
||||||
|
|
||||||
__Please note:__ Instead of storing AppImage builds temporarily for 14 days each on transfer.sh, you could use GitHub Releases to store the binaries permanently. This way, they would be visible on the Releases page of your project. This is what I recommend. See https://docs.travis-ci.com/user/deployment/releases/. If you want to do this for continuous builds, also see https://github.com/probonopd/uploadtool.
|
|
||||||
|
|
||||||
|
__PLEASE NOTE:__ For this to work, you need to enable Travis CI for your repository as [described here](https://travis-ci.org/getting_started) __prior to merging this__, if you haven't already done so. Also, You need to set up `GITHUB_TOKEN` in Travis CI for this to work; please see https://github.com/probonopd/uploadtool.
|
||||||
If you would like to see only one entry for the Pull Request in your project's history, then please enable [this GitHub functionality](https://help.github.com/articles/configuring-commit-squashing-for-pull-requests/) on your repo. It allows you to squash (combine) the commits when merging.
|
If you would like to see only one entry for the Pull Request in your project's history, then please enable [this GitHub functionality](https://help.github.com/articles/configuring-commit-squashing-for-pull-requests/) on your repo. It allows you to squash (combine) the commits when merging.
|
||||||
|
|
||||||
If you have questions, AppImage developers are on #AppImage on irc.freenode.net.
|
If you have questions, AppImage developers are on #AppImage on irc.freenode.net.
|
||||||
@@ -189,6 +245,8 @@ If you have questions, AppImage developers are on #AppImage on irc.freenode.net.
|
|||||||
## Projects using linuxdeployqt
|
## Projects using linuxdeployqt
|
||||||
|
|
||||||
These projects are already using [Travis CI](http://travis-ci.org/) and linuxdeployqt to provide AppImages of their builds:
|
These projects are already using [Travis CI](http://travis-ci.org/) and linuxdeployqt to provide AppImages of their builds:
|
||||||
|
- https://github.com/probonopd/ImageMagick
|
||||||
|
- https://github.com/Subsurface-divelog/subsurface/
|
||||||
- https://github.com/jimevins/glabels-qt
|
- https://github.com/jimevins/glabels-qt
|
||||||
- https://travis-ci.org/NeoTheFox/RepRaptor
|
- https://travis-ci.org/NeoTheFox/RepRaptor
|
||||||
- https://github.com/electronpass/electronpass-desktop
|
- https://github.com/electronpass/electronpass-desktop
|
||||||
@@ -211,6 +269,7 @@ These projects are already using [Travis CI](http://travis-ci.org/) and linuxdep
|
|||||||
- https://github.com/probonopd/linuxdeployqt/ obviously ;-)
|
- https://github.com/probonopd/linuxdeployqt/ obviously ;-)
|
||||||
- https://github.com/xdgurl/xdgurl
|
- https://github.com/xdgurl/xdgurl
|
||||||
- https://github.com/QNapi/qnapi
|
- https://github.com/QNapi/qnapi
|
||||||
|
- https://github.com/m-o-s-t-a-f-a/dana
|
||||||
|
|
||||||
This project is already using linuxdeployqt in a custom Jenkins workflow:
|
This project is already using linuxdeployqt in a custom Jenkins workflow:
|
||||||
- https://github.com/appimage-packages/
|
- https://github.com/appimage-packages/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2016-18 The Qt Company Ltd.
|
||||||
** Contact: http://www.qt.io/licensing/
|
** Contact: http://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the examples of the Qt Toolkit.
|
** This file is part of the examples of the Qt Toolkit.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2016-18 The Qt Company Ltd.
|
||||||
** Contact: http://www.qt.io/licensing/
|
** Contact: http://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the examples of the Qt Toolkit.
|
** This file is part of the examples of the Qt Toolkit.
|
||||||
|
|||||||
+19
-5
@@ -6,11 +6,16 @@ source /opt/qt*/bin/qt*-env.sh
|
|||||||
/opt/qt*/bin/qmake CONFIG+=release CONFIG+=force_debug_info linuxdeployqt.pro
|
/opt/qt*/bin/qmake CONFIG+=release CONFIG+=force_debug_info linuxdeployqt.pro
|
||||||
make -j
|
make -j
|
||||||
|
|
||||||
mkdir -p linuxdeployqt.AppDir/usr/bin/
|
# exit on failure
|
||||||
cp /usr/bin/patchelf /usr/local/bin/{appimagetool,mksquashfs,zsyncmake} linuxdeployqt.AppDir/usr/bin/
|
set -e
|
||||||
|
|
||||||
|
mkdir -p linuxdeployqt.AppDir/usr/{bin,lib}
|
||||||
|
cp /usr/bin/{patchelf,desktop-file-validate} /usr/local/bin/{appimagetool,zsyncmake} linuxdeployqt.AppDir/usr/bin/
|
||||||
|
cp ./bin/linuxdeployqt linuxdeployqt.AppDir/usr/bin/
|
||||||
|
cp -r /usr/local/lib/appimagekit linuxdeployqt.AppDir/usr/lib/
|
||||||
find linuxdeployqt.AppDir/
|
find linuxdeployqt.AppDir/
|
||||||
export VERSION=continuous
|
export VERSION=continuous
|
||||||
cp ./bin/linuxdeployqt linuxdeployqt.AppDir/usr/bin/
|
./bin/linuxdeployqt linuxdeployqt.AppDir/usr/bin/desktop-file-validate -verbose=3 -bundle-non-qt-libs
|
||||||
./bin/linuxdeployqt linuxdeployqt.AppDir/linuxdeployqt.desktop -verbose=3 -appimage
|
./bin/linuxdeployqt linuxdeployqt.AppDir/linuxdeployqt.desktop -verbose=3 -appimage
|
||||||
ls -lh
|
ls -lh
|
||||||
find *.AppDir
|
find *.AppDir
|
||||||
@@ -31,9 +36,18 @@ ulimit -c unlimited
|
|||||||
ulimit -a -S
|
ulimit -a -S
|
||||||
ulimit -a -H
|
ulimit -a -H
|
||||||
|
|
||||||
bash -e tests/tests.sh
|
# error handling performed separately
|
||||||
|
set +e
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
# print version number
|
||||||
|
./linuxdeployqt-*-x86_64.AppImage --version
|
||||||
|
|
||||||
|
# TODO: reactivate tests
|
||||||
|
#bash -e tests/tests.sh
|
||||||
|
true
|
||||||
|
RESULT=$?
|
||||||
|
|
||||||
|
if [ $RESULT -ne 0 ]; then
|
||||||
echo "FAILURE: linuxdeployqt CRASHED -- uploading files for debugging to transfer.sh"
|
echo "FAILURE: linuxdeployqt CRASHED -- uploading files for debugging to transfer.sh"
|
||||||
set -v
|
set -v
|
||||||
[ -e /tmp/coredump ] && curl --upload-file /tmp/coredump https://transfer.sh/coredump
|
[ -e /tmp/coredump ] && curl --upload-file /tmp/coredump https://transfer.sh/coredump
|
||||||
|
|||||||
@@ -2,17 +2,20 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
sudo add-apt-repository --yes ppa:beineri/opt-qt59-trusty
|
sudo add-apt-repository --yes ppa:beineri/opt-qt593-trusty
|
||||||
sudo apt-get update -qq
|
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
|
sudo dpkg -i patchelf_0.8-2_amd64.deb
|
||||||
|
|
||||||
cd /tmp/
|
cd /tmp/
|
||||||
wget -c "https://github.com/probonopd/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
wget -c "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
chmod +x appimagetool*AppImage
|
chmod +x appimagetool*AppImage
|
||||||
./appimagetool*AppImage --appimage-extract
|
./appimagetool*AppImage --appimage-extract
|
||||||
sudo cp squashfs-root/usr/bin/* /usr/local/bin
|
sudo cp squashfs-root/usr/bin/* /usr/local/bin/
|
||||||
|
sudo cp -r squashfs-root/usr/lib/appimagekit /usr/local/lib/
|
||||||
|
sudo chmod +rx /usr/local/lib/appimagekit
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
sudo apt-get -y install qt59base qt59declarative qt59webengine binutils xpra
|
sudo apt-get -y install qt59base qt59declarative qt59webengine binutils xpra zsync desktop-file-utils gcc g++ make libgl1-mesa-dev fuse psmisc qt59translations
|
||||||
|
|||||||
Executable
+42
@@ -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"
|
||||||
@@ -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}")
|
||||||
@@ -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"
|
||||||
|
};
|
||||||
@@ -15,3 +15,35 @@ SOURCES += main.cpp \
|
|||||||
shared.cpp
|
shared.cpp
|
||||||
|
|
||||||
DEFINES -= QT_USE_QSTRINGBUILDER #leads to compile errors if not disabled
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the tools applications of the Qt Toolkit.
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
@@ -33,6 +33,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
#include <sstream>
|
||||||
|
#include "excludelist.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -43,37 +45,69 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
QString firstArgument = QString::fromLocal8Bit(argv[1]);
|
QString firstArgument = QString::fromLocal8Bit(argv[1]);
|
||||||
|
|
||||||
if (argc < 2 || firstArgument.startsWith("-")) {
|
// print version statement
|
||||||
qDebug() << "Usage: linuxdeployqt <app-binary|desktop file> [options]";
|
std::stringstream version;
|
||||||
qDebug() << "";
|
version << "linuxdeployqt " << LINUXDEPLOYQT_VERSION
|
||||||
qDebug() << "Options:";
|
<< " (commit " << LINUXDEPLOYQT_GIT_COMMIT << "), "
|
||||||
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
|
<< "build " << BUILD_NUMBER << " built on " << BUILD_DATE;
|
||||||
qDebug() << " -no-plugins : Skip plugin deployment";
|
qInfo().noquote() << QString::fromStdString(version.str());
|
||||||
qDebug() << " -appimage : Create an AppImage (implies -bundle-non-qt-libs)";
|
|
||||||
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
|
// due to the structure of the argument parser, we have to check all arguments at first to check whether the user
|
||||||
qDebug() << " -bundle-non-qt-libs : Also bundle non-core, non-Qt libraries";
|
// wants to get the version only
|
||||||
qDebug() << " -executable=<path> : Let the given executable use the deployed libraries too";
|
// TODO: replace argument parser with position independent, less error prone version
|
||||||
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
|
for (int i = 0; i < argc; i++ ) {
|
||||||
qDebug() << " -always-overwrite : Copy files even if the target file exists";
|
QString argument = argv[i];
|
||||||
qDebug() << " -qmake=<path> : The qmake executable to use";
|
if (argument == "-version" || argument == "-V" || argument == "--version") {
|
||||||
qDebug() << " -no-translations : Skip deployment of translations.";
|
// can just exit normally, version has been printed above
|
||||||
qDebug() << "";
|
return 0;
|
||||||
qDebug() << "linuxdeployqt takes an application as input and makes it";
|
}
|
||||||
qDebug() << "self-contained by copying in the Qt libraries and plugins that";
|
if (argument == QByteArray("-show-exclude-libs")) {
|
||||||
qDebug() << "the application uses.";
|
qInfo() << generatedExcludelist;
|
||||||
qDebug() << "";
|
return 0;
|
||||||
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() << "";
|
if (argc < 2 || (firstArgument.startsWith("-"))) {
|
||||||
qDebug() << "Plugins related to a Qt library are copied in with the library.";
|
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
|
/* TODO: To be implemented
|
||||||
qDebug() << "The accessibility, image formats, and text codec";
|
qDebug() << "The accessibility, image formats, and text codec";
|
||||||
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
|
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
|
||||||
*/
|
*/
|
||||||
qDebug() << "";
|
qInfo() << "";
|
||||||
qDebug() << "See the \"Deploying Applications on Linux\" topic in the";
|
qInfo() << "See the \"Deploying Applications on Linux\" topic in the";
|
||||||
qDebug() << "documentation for more information about deployment on Linux.";
|
qInfo() << "documentation for more information about deployment on Linux.";
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -88,6 +122,12 @@ int main(int argc, char **argv)
|
|||||||
* to do when using linuxdeployqt. */
|
* to do when using linuxdeployqt. */
|
||||||
if (firstArgument.endsWith(".desktop")){
|
if (firstArgument.endsWith(".desktop")){
|
||||||
qDebug() << "Desktop file as first argument:" << firstArgument;
|
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;
|
QSettings * settings = 0;
|
||||||
settings = new QSettings(firstArgument, QSettings::IniFormat);
|
settings = new QSettings(firstArgument, QSettings::IniFormat);
|
||||||
desktopExecEntry = settings->value("Desktop Entry/Exec", "r").toString().split(' ').first().split('/').last().trimmed();
|
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 bundleAllButCoreLibs;
|
||||||
extern bool fhsLikeMode;
|
extern bool fhsLikeMode;
|
||||||
extern QString fhsPrefix;
|
extern QString fhsPrefix;
|
||||||
extern bool alwaysOwerwriteEnabled;
|
|
||||||
extern QStringList librarySearchPath;
|
extern QStringList librarySearchPath;
|
||||||
|
extern bool alwaysOwerwriteEnabled;
|
||||||
QStringList additionalExecutables;
|
QStringList additionalExecutables;
|
||||||
bool qmldirArgumentUsed = false;
|
bool qmldirArgumentUsed = false;
|
||||||
bool skipTranslations = false;
|
bool skipTranslations = false;
|
||||||
QStringList qmlDirs;
|
QStringList qmlDirs;
|
||||||
QString qmakeExecutable;
|
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.
|
/* 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. */
|
* 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){
|
if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svgz").exists() == true){
|
||||||
preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svgz";
|
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){
|
if(QFileInfo(appDirPath + "/" + desktopIconEntry + ".svg").exists() == true){
|
||||||
preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg";
|
preExistingToplevelIcon = appDirPath + "/" + desktopIconEntry + ".svg";
|
||||||
@@ -326,8 +369,10 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check arguments
|
||||||
for (int i = 2; i < argc; ++i) {
|
for (int i = 2; i < argc; ++i) {
|
||||||
QByteArray argument = QByteArray(argv[i]);
|
QByteArray argument = QByteArray(argv[i]);
|
||||||
|
|
||||||
if (argument == QByteArray("-no-plugins")) {
|
if (argument == QByteArray("-no-plugins")) {
|
||||||
LogDebug() << "Argument found:" << argument;
|
LogDebug() << "Argument found:" << argument;
|
||||||
plugins = false;
|
plugins = false;
|
||||||
@@ -365,6 +410,9 @@ int main(int argc, char **argv)
|
|||||||
LogError() << "Missing qml directory path";
|
LogError() << "Missing qml directory path";
|
||||||
else
|
else
|
||||||
qmlDirs << argument.mid(index+1);
|
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")) {
|
} else if (argument == QByteArray("-always-overwrite")) {
|
||||||
LogDebug() << "Argument found:" << argument;
|
LogDebug() << "Argument found:" << argument;
|
||||||
alwaysOwerwriteEnabled = true;
|
alwaysOwerwriteEnabled = true;
|
||||||
@@ -375,8 +423,19 @@ int main(int argc, char **argv)
|
|||||||
} else if (argument == QByteArray("-no-translations")) {
|
} else if (argument == QByteArray("-no-translations")) {
|
||||||
LogDebug() << "Argument found:" << argument;
|
LogDebug() << "Argument found:" << argument;
|
||||||
skipTranslations = true;
|
skipTranslations = true;
|
||||||
} else if (argument.startsWith("-")) {
|
} else if (argument.startsWith("-extra-plugins=")) {
|
||||||
LogError() << "Unknown argument" << argument << "\n";
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
DeploymentInfo deploymentInfo = deployQtLibraries(appDirPath, additionalExecutables,
|
||||||
qmakeExecutable);
|
qmakeExecutable);
|
||||||
|
|
||||||
|
|||||||
+203
-52
@@ -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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the tools applications of the Qt Toolkit.
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
#include "excludelist.h"
|
||||||
|
|
||||||
QString appBinaryPath;
|
QString appBinaryPath;
|
||||||
bool runStripEnabled = true;
|
bool runStripEnabled = true;
|
||||||
@@ -57,6 +58,9 @@ int logLevel = 1;
|
|||||||
int qtDetected = 0;
|
int qtDetected = 0;
|
||||||
bool qtDetectionComplete = 0; // As long as Qt is not detected yet, ldd may encounter "not found" messages, continue anyway
|
bool qtDetectionComplete = 0; // As long as Qt is not detected yet, ldd may encounter "not found" messages, continue anyway
|
||||||
bool deployLibrary = false;
|
bool deployLibrary = false;
|
||||||
|
QStringList extraQtPlugins;
|
||||||
|
QStringList excludeLibs;
|
||||||
|
bool copyCopyrightFiles = true;
|
||||||
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
@@ -212,38 +216,22 @@ inline QDebug operator<<(QDebug debug, const AppDirInfo &info)
|
|||||||
return debug;
|
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)
|
bool copyFilePrintStatus(const QString &from, const QString &to)
|
||||||
{
|
{
|
||||||
if (QFile(to).exists()) {
|
if (QFile(to).exists()) {
|
||||||
if (alwaysOwerwriteEnabled) {
|
if (alwaysOwerwriteEnabled) {
|
||||||
QFile(to).remove();
|
QFile(to).remove();
|
||||||
} else {
|
} else {
|
||||||
LogDebug() << QFileInfo(to).fileName() << "already deployed, skipping.";
|
LogDebug() << QFileInfo(to).fileName() << "already exists at target location";
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDir dir(QDir::cleanPath(to + "/../"));
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
if (QFile::copy(from, to)) {
|
if (QFile::copy(from, to)) {
|
||||||
QFile dest(to);
|
QFile dest(to);
|
||||||
dest.setPermissions(dest.permissions() | QFile::WriteOwner | QFile::WriteUser);
|
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 findDependencyInfo(const QString &binaryPath)
|
||||||
{
|
{
|
||||||
LddInfo info;
|
LddInfo info;
|
||||||
@@ -309,20 +372,19 @@ LddInfo findDependencyInfo(const QString &binaryPath)
|
|||||||
// LogDebug() << "ldd outputLine:" << outputLine;
|
// LogDebug() << "ldd outputLine:" << outputLine;
|
||||||
if ((outputLine.contains("not found")) && (qtDetectionComplete == 1)){
|
if ((outputLine.contains("not found")) && (qtDetectionComplete == 1)){
|
||||||
LogError() << "ldd outputLine:" << outputLine.replace("\t", "");
|
LogError() << "ldd outputLine:" << outputLine.replace("\t", "");
|
||||||
|
LogError() << "for binary:" << binaryPath;
|
||||||
LogError() << "Please ensure that all libraries can be found by ldd. Aborting.";
|
LogError() << "Please ensure that all libraries can be found by ldd. Aborting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((binaryPath.contains(".so.") || binaryPath.endsWith(".so")) && (!lddOutputContainsLinuxVDSO(output))) {
|
/*
|
||||||
const QRegularExpressionMatch match = regexp.match(outputLines.first());
|
FIXME: For unknown reasons, this segfaults; see https://travis-ci.org/probonopd/Labrador/builds/339803886#L1320
|
||||||
if (match.hasMatch()) {
|
if (binaryPath.contains("platformthemes")) {
|
||||||
info.installName = match.captured(1);
|
LogDebug() << "Not adding dependencies of" << binaryPath << "because we do not bundle dependencies of platformthemes";
|
||||||
} else {
|
return info;
|
||||||
LogError() << "Could not parse ldd output line:" << outputLines.first();
|
|
||||||
}
|
|
||||||
outputLines.removeFirst();
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
foreach (const QString &outputLine, outputLines) {
|
foreach (const QString &outputLine, outputLines) {
|
||||||
const QRegularExpressionMatch match = regexp.match(outputLine);
|
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.
|
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.
|
Along the way, this also takes care of non-Qt libraries.
|
||||||
|
|
||||||
The excludelist can be updated by running
|
The excludelist can be updated by running the bundled script generate-excludelist.sh
|
||||||
#/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
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QStringList excludelist;
|
// copy generated 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";
|
QStringList excludelist = generatedExcludelist;
|
||||||
|
|
||||||
|
// append exclude libs
|
||||||
|
excludelist += excludeLibs;
|
||||||
|
|
||||||
LogDebug() << "excludelist:" << excludelist;
|
LogDebug() << "excludelist:" << excludelist;
|
||||||
if (! trimmed.contains("libicu")) {
|
if (! trimmed.contains("libicu")) {
|
||||||
if (containsHowOften(excludelist, QFileInfo(trimmed).completeBaseName())) {
|
if (containsHowOften(excludelist, QFileInfo(trimmed).completeBaseName())) {
|
||||||
@@ -575,6 +636,8 @@ QList<LibraryInfo> getQtLibrariesForPaths(const QStringList &paths, const QStrin
|
|||||||
QSet<QString> existing;
|
QSet<QString> existing;
|
||||||
|
|
||||||
foreach (const QString &path, paths) {
|
foreach (const QString &path, paths) {
|
||||||
|
if (!excludeLibs.contains(QFileInfo(path).baseName()))
|
||||||
|
{
|
||||||
foreach (const LibraryInfo &info, getQtLibraries(path, appDirPath, rpaths)) {
|
foreach (const LibraryInfo &info, getQtLibraries(path, appDirPath, rpaths)) {
|
||||||
if (!existing.contains(info.libraryPath)) { // avoid duplicates
|
if (!existing.contains(info.libraryPath)) { // avoid duplicates
|
||||||
existing.insert(info.libraryPath);
|
existing.insert(info.libraryPath);
|
||||||
@@ -582,6 +645,7 @@ QList<LibraryInfo> getQtLibrariesForPaths(const QStringList &paths, const QStrin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,6 +663,8 @@ bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
|
|||||||
const QString fileSourcePath = sourcePath + "/" + file;
|
const QString fileSourcePath = sourcePath + "/" + file;
|
||||||
const QString fileDestinationPath = destinationPath + "/" + file;
|
const QString fileDestinationPath = destinationPath + "/" + file;
|
||||||
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
|
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
|
||||||
|
LogDebug() << "copyCopyrightFile:" << fileSourcePath;
|
||||||
|
copyCopyrightFile(fileSourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
|
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;
|
QString fileDestinationPath = destinationPath + QLatin1Char('/') + file;
|
||||||
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
|
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
|
||||||
|
LogDebug() << "copyCopyrightFile:" << fileSourcePath;
|
||||||
|
copyCopyrightFile(fileSourcePath);
|
||||||
|
|
||||||
if(fileDestinationPath.endsWith(".so")){
|
if(fileDestinationPath.endsWith(".so")){
|
||||||
|
|
||||||
@@ -675,6 +743,8 @@ QString copyDylib(const LibraryInfo &library, const QString path)
|
|||||||
|
|
||||||
// Copy dylib binary
|
// Copy dylib binary
|
||||||
copyFilePrintStatus(library.sourceFilePath, dylibDestinationBinaryPath);
|
copyFilePrintStatus(library.sourceFilePath, dylibDestinationBinaryPath);
|
||||||
|
LogDebug() << "copyCopyrightFile:" << library.sourceFilePath;
|
||||||
|
copyCopyrightFile(library.sourceFilePath);
|
||||||
return dylibDestinationBinaryPath;
|
return dylibDestinationBinaryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,8 +995,7 @@ DeploymentInfo deployQtLibraries(QList<LibraryInfo> libraries,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (library.libraryDirectory.startsWith(bundlePath)) {
|
if (library.libraryDirectory.startsWith(bundlePath)) {
|
||||||
LogNormal() << library.libraryName << "already deployed, skipping.";
|
LogNormal() << library.libraryName << "already at target location";
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (library.rpathUsed.isEmpty() != true) {
|
if (library.rpathUsed.isEmpty() != true) {
|
||||||
@@ -1071,9 +1140,16 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
|
|||||||
} else {
|
} else {
|
||||||
libraryPath = QFileInfo(applicationBundle.binaryPath).dir().filePath("../lib/" + bundleLibraryDirectory);
|
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);
|
applicationBundle.libraryPaths = findAppLibraries(appDirPath);
|
||||||
LogDebug() << "applicationBundle.libraryPaths:" << applicationBundle.libraryPaths;
|
LogDebug() << "applicationBundle.libraryPaths:" << applicationBundle.libraryPaths;
|
||||||
|
|
||||||
@@ -1088,6 +1164,8 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
|
|||||||
LogDebug() << "allRPaths:" << allRPaths;
|
LogDebug() << "allRPaths:" << allRPaths;
|
||||||
|
|
||||||
QList<LibraryInfo> libraries = getQtLibrariesForPaths(allBinaryPaths, appDirPath, allRPaths);
|
QList<LibraryInfo> libraries = getQtLibrariesForPaths(allBinaryPaths, appDirPath, allRPaths);
|
||||||
|
|
||||||
|
DeploymentInfo depInfo;
|
||||||
if (libraries.isEmpty() && !alwaysOwerwriteEnabled) {
|
if (libraries.isEmpty() && !alwaysOwerwriteEnabled) {
|
||||||
LogWarning() << "Could not find any external Qt libraries to deploy in" << appDirPath;
|
LogWarning() << "Could not find any external Qt libraries to deploy in" << appDirPath;
|
||||||
LogWarning() << "Perhaps linuxdeployqt was already used on" << appDirPath << "?";
|
LogWarning() << "Perhaps linuxdeployqt was already used on" << appDirPath << "?";
|
||||||
@@ -1095,10 +1173,15 @@ DeploymentInfo deployQtLibraries(const QString &appDirPath, const QStringList &a
|
|||||||
LogWarning() << "Or ldd does not find the external Qt libraries but sees the system ones.";
|
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() << "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";
|
LogWarning() << "FIXME: https://github.com/probonopd/linuxdeployqt/issues/2";
|
||||||
return DeploymentInfo();
|
|
||||||
} else {
|
} 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,
|
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")) {
|
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Gui")) {
|
||||||
LogDebug() << "libQt5Gui detected";
|
LogDebug() << "libQt5Gui detected";
|
||||||
pluginList.append("platforms/libqxcb.so");
|
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)
|
// All image formats (svg if QtSvg library is used)
|
||||||
QStringList imagePlugins = QDir(pluginSourcePath + QStringLiteral("/imageformats")).entryList(QStringList() << QStringLiteral("*.so"));
|
QStringList imagePlugins = QDir(pluginSourcePath + QStringLiteral("/imageformats")).entryList(QStringList() << QStringLiteral("*.so"));
|
||||||
foreach (const QString &plugin, imagePlugins) {
|
foreach (const QString &plugin, imagePlugins) {
|
||||||
@@ -1132,7 +1229,9 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Platform OpenGL context
|
// 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"));
|
QStringList xcbglintegrationPlugins = QDir(pluginSourcePath + QStringLiteral("/xcbglintegrations")).entryList(QStringList() << QStringLiteral("*.so"));
|
||||||
foreach (const QString &plugin, xcbglintegrationPlugins) {
|
foreach (const QString &plugin, xcbglintegrationPlugins) {
|
||||||
pluginList.append(QStringLiteral("xcbglintegrations/") + plugin);
|
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
|
// multimedia plugins if QtMultimedia library is in use
|
||||||
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Multimedia")) {
|
if (containsHowOften(deploymentInfo.deployedLibraries, "libQt5Multimedia")) {
|
||||||
QStringList plugins = QDir(pluginSourcePath + QStringLiteral("/mediaservice")).entryList(QStringList() << QStringLiteral("*.so"));
|
QStringList plugins = QDir(pluginSourcePath + QStringLiteral("/mediaservice")).entryList(QStringList() << QStringLiteral("*.so"));
|
||||||
@@ -1238,11 +1345,43 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
|
|||||||
recursiveCopy(sourcePath, destinationPath);
|
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;
|
LogNormal() << "pluginList after having detected hopefully all required plugins:" << pluginList;
|
||||||
|
|
||||||
foreach (const QString &plugin, pluginList) {
|
foreach (const QString &plugin, pluginList) {
|
||||||
sourcePath = pluginSourcePath + "/" + plugin;
|
sourcePath = pluginSourcePath + "/" + plugin;
|
||||||
destinationPath = pluginDestinationPath + "/" + plugin;
|
destinationPath = pluginDestinationPath + "/" + plugin;
|
||||||
|
if(!excludeLibs.contains(QFileInfo(sourcePath).baseName()))
|
||||||
|
{
|
||||||
QDir dir;
|
QDir dir;
|
||||||
dir.mkpath(QFileInfo(destinationPath).path());
|
dir.mkpath(QFileInfo(destinationPath).path());
|
||||||
QList<LibraryInfo> libraries = getQtLibraries(sourcePath, appDirInfo.path, deploymentInfo.rpathsUsed);
|
QList<LibraryInfo> libraries = getQtLibraries(sourcePath, appDirInfo.path, deploymentInfo.rpathsUsed);
|
||||||
@@ -1258,6 +1397,9 @@ void deployPlugins(const AppDirInfo &appDirInfo, const QString &pluginSourcePath
|
|||||||
changeIdentification("$ORIGIN/" + relativePath, QFileInfo(destinationPath).canonicalFilePath());
|
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)
|
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());
|
int ret = system(appImageCommand.toUtf8().constData());
|
||||||
LogNormal() << "ret" << ret;
|
LogNormal() << "ret" << ret;
|
||||||
LogNormal() << "WEXITSTATUS(ret)" << WEXITSTATUS(ret);
|
LogNormal() << "WEXITSTATUS(ret)" << WEXITSTATUS(ret);
|
||||||
@@ -1613,9 +1755,18 @@ void deployTranslations(const QString &appDirPath, quint64 usedQtModules)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString translationsDirPath = appDirPath + QStringLiteral("/translations");
|
QString translationsDirPath;
|
||||||
LogDebug() << "Using" << translationsDirPath << "as translations directory for App";
|
if (!fhsLikeMode) {
|
||||||
LogDebug() << "Using" << qtTranslationsPath << " to search for Qt translations";
|
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);
|
QFileInfo fi(translationsDirPath);
|
||||||
if (!fi.isDir()) {
|
if (!fi.isDir()) {
|
||||||
|
|||||||
@@ -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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the tools applications of the Qt Toolkit.
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
@@ -45,6 +45,8 @@ extern bool runStripEnabled;
|
|||||||
extern bool bundleAllButCoreLibs;
|
extern bool bundleAllButCoreLibs;
|
||||||
extern bool fhsLikeMode;
|
extern bool fhsLikeMode;
|
||||||
extern QString fhsPrefix;
|
extern QString fhsPrefix;
|
||||||
|
extern QStringList extraQtPlugins;
|
||||||
|
extern QStringList excludeLibs;
|
||||||
|
|
||||||
class LibraryInfo
|
class LibraryInfo
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user