Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/scripts/packaging/appimage/make-appimage.sh
4804 views
1
#!/usr/bin/env bash
2
3
# SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
4
# SPDX-License-Identifier: CC-BY-NC-ND-4.0
5
6
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
7
8
function retry_command {
9
# Package servers tend to be unreliable at times..
10
# Retry a bunch of times.
11
local RETRIES=10
12
13
for i in $(seq 1 "$RETRIES"); do
14
"$@" && break
15
if [ "$i" == "$RETRIES" ]; then
16
echo "Command \"$@\" failed after ${RETRIES} retries."
17
exit 1
18
fi
19
done
20
}
21
22
if [ "$#" -ne 4 ]; then
23
echo "Syntax: $0 <path to duckstation directory> <path to build directory> <deps prefix> <output name>"
24
exit 1
25
fi
26
27
ROOTDIR=$1
28
BUILDDIR=$2
29
DEPSDIR=$3
30
ASSETNAME=$4
31
32
BINARY=duckstation-qt
33
APPDIRNAME=DuckStation.AppDir
34
STRIP=strip
35
36
declare -a MANUAL_LIBS=(
37
"libz.so.1"
38
"libavcodec.so.61"
39
"libavformat.so.61"
40
"libavutil.so.59"
41
"libswscale.so.8"
42
"libswresample.so.5"
43
"libdiscord-rpc.so"
44
"libharfbuzz.so"
45
"libfreetype.so.6"
46
"libshaderc_ds.so"
47
"libspirv-cross-c-shared.so.0"
48
)
49
50
declare -a MANUAL_QT_LIBS=(
51
"libQt6WaylandEglClientHwIntegration.so.6"
52
)
53
54
declare -a MANUAL_QT_PLUGINS=(
55
"wayland-decoration-client"
56
"wayland-graphics-integration-client"
57
"wayland-shell-integration"
58
)
59
60
declare -a REMOVE_LIBS=(
61
'libwayland-client.so*'
62
'libwayland-cursor.so*'
63
'libwayland-egl.so*'
64
)
65
66
set -e
67
68
LINUXDEPLOY=./linuxdeploy-x86_64
69
LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64
70
APPIMAGETOOL=./appimagetool-x86_64
71
APPIMAGERUNTIME=./runtime-x86_64
72
PATCHELF=patchelf
73
74
if [ ! -f "$LINUXDEPLOY" ]; then
75
retry_command wget -O "$LINUXDEPLOY" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/linuxdeploy-x86_64.AppImage
76
chmod +x "$LINUXDEPLOY"
77
fi
78
79
if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then
80
retry_command wget -O "$LINUXDEPLOY_PLUGIN_QT" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/linuxdeploy-plugin-qt-x86_64.AppImage
81
chmod +x "$LINUXDEPLOY_PLUGIN_QT"
82
fi
83
84
if [ ! -f "$APPIMAGETOOL" ]; then
85
retry_command wget -O "$APPIMAGETOOL" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/appimagetool-x86_64.AppImage
86
chmod +x "$APPIMAGETOOL"
87
fi
88
89
if [ ! -f "$APPIMAGERUNTIME" ]; then
90
retry_command wget -O "$APPIMAGERUNTIME" https://github.com/stenzek/type2-runtime/releases/download/continuous/runtime-x86_64
91
fi
92
93
OUTDIR=$(realpath "./$APPDIRNAME")
94
rm -fr "$OUTDIR"
95
96
echo "Locating extra libraries..."
97
EXTRA_LIBS_ARGS=()
98
for lib in "${MANUAL_LIBS[@]}"; do
99
srcpath=$(find "$DEPSDIR" -name "$lib")
100
if [ ! -f "$srcpath" ]; then
101
echo "Missinge extra library $lib. Exiting."
102
exit 1
103
fi
104
105
echo "Found $lib at $srcpath."
106
EXTRA_LIBS_ARGS+=("--library=$srcpath")
107
done
108
109
# Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off.
110
# It also doesn't strip the Qt libs. We can't strip them after running linuxdeploy, because
111
# patchelf corrupts the libraries (but they still work), but patchelf+strip makes them crash
112
# on load. So, make a backup copy, strip the original (since that's where linuxdeploy finds
113
# the libs to copy), then swap them back after we're done.
114
# Isn't Linux packaging amazing?
115
116
rm -fr "$DEPSDIR.bak"
117
cp -a "$DEPSDIR" "$DEPSDIR.bak"
118
IFS="
119
"
120
for i in $(find "$DEPSDIR" -iname '*.so'); do
121
echo "Stripping deps library ${i}"
122
strip "$i"
123
done
124
125
echo "Running linuxdeploy to create AppDir..."
126
EXTRA_QT_PLUGINS="core;gui;svg;waylandclient;widgets;xcbqpa" \
127
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
128
DEPLOY_PLATFORM_THEMES="1" \
129
QMAKE="$DEPSDIR/bin/qmake" \
130
NO_STRIP="1" \
131
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" ${EXTRA_LIBS_ARGS[@]} \
132
--desktop-file="$ROOTDIR/scripts/packaging/org.duckstation.DuckStation.desktop" \
133
--icon-file="$ROOTDIR/scripts/packaging/org.duckstation.DuckStation.png" \
134
135
echo "Copying resources into AppDir..."
136
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
137
138
# LinuxDeploy's Qt plugin doesn't include Wayland support. So manually copy in the additional Wayland libraries.
139
echo "Copying Qt Wayland libraries..."
140
for lib in "${MANUAL_QT_LIBS[@]}"; do
141
srcpath="$DEPSDIR/lib/$lib"
142
dstpath="$OUTDIR/usr/lib/$lib"
143
echo " $srcpath -> $dstpath"
144
cp "$srcpath" "$dstpath"
145
$PATCHELF --set-rpath '$ORIGIN' "$dstpath"
146
done
147
148
# .. and plugins.
149
echo "Copying Qt Wayland plugins..."
150
for GROUP in "${MANUAL_QT_PLUGINS[@]}"; do
151
srcpath="$DEPSDIR/plugins/$GROUP"
152
dstpath="$OUTDIR/usr/plugins/$GROUP"
153
echo " $srcpath -> $dstpath"
154
mkdir -p "$dstpath"
155
156
for srcsopath in $(find "$DEPSDIR/plugins/$GROUP" -iname '*.so'); do
157
# This is ../../ because it's usually plugins/group/name.so
158
soname=$(basename "$srcsopath")
159
dstsopath="$dstpath/$soname"
160
echo " $srcsopath -> $dstsopath"
161
cp "$srcsopath" "$dstsopath"
162
$PATCHELF --set-rpath '$ORIGIN/../../lib:$ORIGIN' "$dstsopath"
163
done
164
done
165
166
# Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin
167
# copies them, not the "main" linuxdeploy binary, and plugins don't inherit the
168
# include list...
169
for lib in "${REMOVE_LIBS[@]}"; do
170
for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do
171
echo " Removing problematic library ${libpath}."
172
rm -f "$libpath"
173
done
174
done
175
176
# Restore unstripped deps (for cache).
177
rm -fr "$DEPSDIR"
178
mv "$DEPSDIR.bak" "$DEPSDIR"
179
180
# Fix up translations.
181
rm -fr "$OUTDIR/usr/bin/translations"
182
mv "$OUTDIR/usr/translations" "$OUTDIR/usr/bin"
183
cp -a "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin"
184
185
# Generate AppStream meta-info.
186
echo "Generating AppStream metainfo..."
187
mkdir -p "$OUTDIR/usr/share/metainfo"
188
"$SCRIPTDIR/../generate-metainfo.sh" "$OUTDIR/usr/share/metainfo"
189
190
echo "Generating AppImage..."
191
rm -f "$ASSETNAME"
192
"$APPIMAGETOOL" -v --runtime-file "$APPIMAGERUNTIME" "$OUTDIR" "$ASSETNAME"
193
194