make-appimage.sh (7055B)
1 #!/usr/bin/env bash 2 3 # This is free and unencumbered software released into the public domain. 4 # 5 # Anyone is free to copy, modify, publish, use, compile, sell, or 6 # distribute this software, either in source code form or as a compiled 7 # binary, for any purpose, commercial or non-commercial, and by any 8 # means. 9 # 10 # In jurisdictions that recognize copyright laws, the author or authors 11 # of this software dedicate any and all copyright interest in the 12 # software to the public domain. We make this dedication for the benefit 13 # of the public at large and to the detriment of our heirs and 14 # successors. We intend this dedication to be an overt act of 15 # relinquishment in perpetuity of all present and future rights to this 16 # software under copyright law. 17 # 18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 # OTHER DEALINGS IN THE SOFTWARE. 25 # 26 # For more information, please refer to <http://unlicense.org/> 27 28 SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") 29 30 function retry_command { 31 # Package servers tend to be unreliable at times.. 32 # Retry a bunch of times. 33 local RETRIES=10 34 35 for i in $(seq 1 "$RETRIES"); do 36 "$@" && break 37 if [ "$i" == "$RETRIES" ]; then 38 echo "Command \"$@\" failed after ${RETRIES} retries." 39 exit 1 40 fi 41 done 42 } 43 44 if [ "$#" -ne 4 ]; then 45 echo "Syntax: $0 <path to duckstation directory> <path to build directory> <deps prefix> <output name>" 46 exit 1 47 fi 48 49 ROOTDIR=$1 50 BUILDDIR=$2 51 DEPSDIR=$3 52 NAME=$4 53 54 BINARY=duckstation-qt 55 APPDIRNAME=DuckStation.AppDir 56 STRIP=strip 57 58 declare -a MANUAL_LIBS=( 59 "libdiscord-rpc.so" 60 "libfreetype.so.6" 61 "libshaderc_shared.so" 62 "libspirv-cross-c-shared.so.0" 63 ) 64 65 declare -a MANUAL_QT_LIBS=( 66 "libQt6WaylandEglClientHwIntegration.so.6" 67 ) 68 69 declare -a MANUAL_QT_PLUGINS=( 70 "wayland-decoration-client" 71 "wayland-graphics-integration-client" 72 "wayland-shell-integration" 73 ) 74 75 declare -a REMOVE_LIBS=( 76 'libwayland-client.so*' 77 'libwayland-cursor.so*' 78 'libwayland-egl.so*' 79 ) 80 81 set -e 82 83 LINUXDEPLOY=./linuxdeploy-x86_64 84 LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64 85 APPIMAGETOOL=./appimagetool-x86_64 86 APPIMAGERUNTIME=./runtime-x86_64 87 PATCHELF=patchelf 88 89 if [ ! -f "$LINUXDEPLOY" ]; then 90 retry_command wget -O "$LINUXDEPLOY" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/linuxdeploy-x86_64.AppImage 91 chmod +x "$LINUXDEPLOY" 92 fi 93 94 if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then 95 retry_command wget -O "$LINUXDEPLOY_PLUGIN_QT" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/linuxdeploy-plugin-qt-x86_64.AppImage 96 chmod +x "$LINUXDEPLOY_PLUGIN_QT" 97 fi 98 99 if [ ! -f "$APPIMAGETOOL" ]; then 100 retry_command wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage 101 chmod +x "$APPIMAGETOOL" 102 fi 103 104 if [ ! -f "$APPIMAGERUNTIME" ]; then 105 retry_command wget -O "$APPIMAGERUNTIME" https://github.com/stenzek/type2-runtime/releases/download/continuous/runtime-x86_64 106 fi 107 108 OUTDIR=$(realpath "./$APPDIRNAME") 109 rm -fr "$OUTDIR" 110 111 echo "Locating extra libraries..." 112 EXTRA_LIBS_ARGS=() 113 for lib in "${MANUAL_LIBS[@]}"; do 114 srcpath=$(find "$DEPSDIR" -name "$lib") 115 if [ ! -f "$srcpath" ]; then 116 echo "Missinge extra library $lib. Exiting." 117 exit 1 118 fi 119 120 echo "Found $lib at $srcpath." 121 EXTRA_LIBS_ARGS+=("--library=$srcpath") 122 done 123 124 # Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off. 125 # It also doesn't strip the Qt libs. We can't strip them after running linuxdeploy, because 126 # patchelf corrupts the libraries (but they still work), but patchelf+strip makes them crash 127 # on load. So, make a backup copy, strip the original (since that's where linuxdeploy finds 128 # the libs to copy), then swap them back after we're done. 129 # Isn't Linux packaging amazing? 130 131 rm -fr "$DEPSDIR.bak" 132 cp -a "$DEPSDIR" "$DEPSDIR.bak" 133 IFS=" 134 " 135 for i in $(find "$DEPSDIR" -iname '*.so'); do 136 echo "Stripping deps library ${i}" 137 strip "$i" 138 done 139 140 echo "Running linuxdeploy to create AppDir..." 141 EXTRA_QT_PLUGINS="core;gui;svg;waylandclient;widgets;xcbqpa" \ 142 EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \ 143 DEPLOY_PLATFORM_THEMES="1" \ 144 QMAKE="$DEPSDIR/bin/qmake" \ 145 NO_STRIP="1" \ 146 $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" ${EXTRA_LIBS_ARGS[@]} \ 147 --desktop-file="$ROOTDIR/scripts/org.duckstation.DuckStation.desktop" \ 148 --icon-file="$ROOTDIR/scripts/org.duckstation.DuckStation.png" \ 149 150 echo "Copying resources into AppDir..." 151 cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin" 152 153 # LinuxDeploy's Qt plugin doesn't include Wayland support. So manually copy in the additional Wayland libraries. 154 echo "Copying Qt Wayland libraries..." 155 for lib in "${MANUAL_QT_LIBS[@]}"; do 156 srcpath="$DEPSDIR/lib/$lib" 157 dstpath="$OUTDIR/usr/lib/$lib" 158 echo " $srcpath -> $dstpath" 159 cp "$srcpath" "$dstpath" 160 $PATCHELF --set-rpath '$ORIGIN' "$dstpath" 161 done 162 163 # .. and plugins. 164 echo "Copying Qt Wayland plugins..." 165 for GROUP in "${MANUAL_QT_PLUGINS[@]}"; do 166 srcpath="$DEPSDIR/plugins/$GROUP" 167 dstpath="$OUTDIR/usr/plugins/$GROUP" 168 echo " $srcpath -> $dstpath" 169 mkdir -p "$dstpath" 170 171 for srcsopath in $(find "$DEPSDIR/plugins/$GROUP" -iname '*.so'); do 172 # This is ../../ because it's usually plugins/group/name.so 173 soname=$(basename "$srcsopath") 174 dstsopath="$dstpath/$soname" 175 echo " $srcsopath -> $dstsopath" 176 cp "$srcsopath" "$dstsopath" 177 $PATCHELF --set-rpath '$ORIGIN/../../lib:$ORIGIN' "$dstsopath" 178 done 179 done 180 181 # Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin 182 # copies them, not the "main" linuxdeploy binary, and plugins don't inherit the 183 # include list... 184 for lib in "${REMOVE_LIBS[@]}"; do 185 for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do 186 echo " Removing problematic library ${libpath}." 187 rm -f "$libpath" 188 done 189 done 190 191 # Restore unstripped deps (for cache). 192 rm -fr "$DEPSDIR" 193 mv "$DEPSDIR.bak" "$DEPSDIR" 194 195 # Fix up translations. 196 rm -fr "$OUTDIR/usr/bin/translations" 197 mv "$OUTDIR/usr/translations" "$OUTDIR/usr/bin" 198 cp -a "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin" 199 200 # Generate AppStream meta-info. 201 echo "Generating AppStream metainfo..." 202 mkdir -p "$OUTDIR/usr/share/metainfo" 203 "$SCRIPTDIR/../generate-metainfo.sh" "$OUTDIR/usr/share/metainfo" 204 205 # Copy in AppRun hooks. 206 echo "Copying AppRun hooks..." 207 mkdir -p "$OUTDIR/apprun-hooks" 208 for hookpath in "$SCRIPTDIR/apprun-hooks"/*; do 209 hookname=$(basename "$hookpath") 210 cp -v "$hookpath" "$OUTDIR/apprun-hooks/$hookname" 211 sed -i -e 's/exec /source "$this_dir"\/apprun-hooks\/"'"$hookname"'"\nexec /' "$OUTDIR/AppRun" 212 done 213 214 echo "Generating AppImage..." 215 rm -f "$NAME.AppImage" 216 "$APPIMAGETOOL" -v --runtime-file "$APPIMAGERUNTIME" "$OUTDIR" "$NAME.AppImage"