diff --git a/.github/workflows/linux_debug.yml b/.github/workflows/linux_debug.yml index 735cd04..9849152 100644 --- a/.github/workflows/linux_debug.yml +++ b/.github/workflows/linux_debug.yml @@ -45,6 +45,9 @@ jobs: - name: Install ccache run: sudo apt-get install ccache + - name: Install post-link dependencies + run: sudo apt-get install -y patchelf + - name: Prepare ccache timestamp id: ccache_cache_timestamp shell: cmake -P {0} diff --git a/.github/workflows/linux_release.yml b/.github/workflows/linux_release.yml index ff85ec7..abe7f35 100644 --- a/.github/workflows/linux_release.yml +++ b/.github/workflows/linux_release.yml @@ -46,6 +46,9 @@ jobs: - name: Install ccache run: sudo apt-get install ccache + - name: Install post-link dependencies + run: sudo apt-get install -y patchelf + - name: Prepare ccache timestamp id: ccache_cache_timestamp shell: cmake -P {0} diff --git a/QGCPostLinkCommon.pri b/QGCPostLinkCommon.pri index 1634069..c90cf95 100644 --- a/QGCPostLinkCommon.pri +++ b/QGCPostLinkCommon.pri @@ -163,4 +163,6 @@ LinuxBuild { } else { include($$PWD/custom/custom_deploy.pri) } + + QMAKE_POST_LINK += && QTDIR="$$DESTDIR/Qt" RPATHDIR="$$DESTDIR/Qt/libs" QTCONF_PATH="$$DESTDIR/qt.conf" $$SOURCE_DIR/deploy/linux-post-link.sh } diff --git a/deploy/docker/Dockerfile-build-linux b/deploy/docker/Dockerfile-build-linux index 538a96f..6c31b9a 100644 --- a/deploy/docker/Dockerfile-build-linux +++ b/deploy/docker/Dockerfile-build-linux @@ -47,6 +47,7 @@ RUN apt update && apt -y --quiet --no-install-recommends install \ ninja-build \ openssh-client \ openssl \ + patchelf \ pkg-config \ rsync \ speech-dispatcher \ diff --git a/deploy/linux-post-link.sh b/deploy/linux-post-link.sh new file mode 100755 index 0000000..a0e6d0e --- /dev/null +++ b/deploy/linux-post-link.sh @@ -0,0 +1,72 @@ +#!/usr/bin/bash +# +# Premise: +# Shared libraries (Qt, airmap, etc) on Linux are built without knowing where +# they will be installed to; they assume they will be installed to the system. +# +# QGC does not install to the system. Instead it copies them to `Qt/libs`. The +# libraries need to have their rpath set correctly to ensure that the built +# application and its libraries are found at runtime. Without this step then the +# libraries won't be found. You might not notice it if you have the libraries +# installed in your system. A lot of systems will have the Qt libs installed, +# but they might be a different version. It "might" work, it might cause subtle +# bugs, or it might not work at all. +# +# To patch the libraries, a tool called `patchelf` is used. +# +# If the libraries' rpath isn't set correctly then LD_LIBRARY_PATH would need +# to be used, like such: +# LD_LIBRARY_PATH=./Qt/libs ./QGroundControl +# +# In addition, Qt will sometimes want to reference its own directory to find +# certain resources. This installs a file, qt.conf, which tells Qt where its +# installation is at. +# Without the qt.conf file, you would need to tell Qt where to find certain +# files, particularly for QML, like such: +# QML2_IMPORT_PATH=./Qt/qml QT_PLUGIN_PATH=./Qt/plugins ./QGroundControl +# + +# -x: echo +# -e: stop on error +# -u: undefined variable use is an error +# -o pipefail: if any part of a pipeline fails, then the whole pipeline fails. +set -xeuo pipefail + +# To set these arguments, set them as an environment variable. For example: +# QTDIR=/opt/qgc-deploy/Qt RPATHDIR=/opt/qgc-deploy/Qt/libs QTCONF_PATH=/opt/qgc-deploy/qt.conf ./linux-post-link.sh +: "${QTDIR:=./Qt}" +: "${RPATHDIR:="${QTDIR}/libs"}" +: "${QTCONF_PATH:=./qt.conf}" + +# find: +# type f (files) +# that end with '.so' +# or that end with '.so.5' +# and are executable +# silence stderr (find will complain if it doesn't have permission to traverse) +find "${QTDIR}" \ + -type f \ + -iname '*.so' \ + -o -iname '*.so.5' \ + -executable \ + 2>/dev/null | +while IFS='' read -r library; do + # Get the directory containing the library + library_dir="$(dirname "${library}")" + + # Get the relative path from the library's directory to the Qt/libs directory. + library_rpath="$(realpath --relative-to "${library_dir}" "${RPATHDIR}")" + + # patch the library's rpath to point to the Qt/libs directory. + # Note: '$ORIGIN' must not be expanded by the shell! + # shellcheck disable=SC2016 + patchelf --set-rpath '$ORIGIN/'"${library_rpath}" "${library}" +done + +# Create a qt.conf file +# https://doc.qt.io/qt-5/qt-conf.html +cat < "${QTCONF_PATH}" +[Paths] +Prefix=./Qt +Libraries=libs +EOF diff --git a/deploy/qgroundcontrol-start.sh b/deploy/qgroundcontrol-start.sh index 9f04ca8..6029f23 100755 --- a/deploy/qgroundcontrol-start.sh +++ b/deploy/qgroundcontrol-start.sh @@ -1,10 +1,7 @@ #!/bin/sh HERE="$(dirname "$(readlink -f "${0}")")" -export LD_LIBRARY_PATH="${HERE}/usr/lib/x86_64-linux-gnu":"${HERE}/Qt/libs":$LD_LIBRARY_PATH -export QML2_IMPORT_PATH="${HERE}/Qt/qml" -export QT_PLUGIN_PATH="${HERE}/Qt/plugins" # hack until icon issue with AppImage is resolved -mkdir -p ~/.icons && cp ${HERE}/qgroundcontrol.png ~/.icons +mkdir -p ~/.icons && cp "${HERE}/qgroundcontrol.png" ~/.icons "${HERE}/QGroundControl" "$@"