Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/scripts/packaging/appimage/make-cross-appimage.sh
4251 views
1
#!/usr/bin/env bash
2
3
# SPDX-FileCopyrightText: 2019-2024 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 [ "$1" == "-inject-libc" ]; then
23
echo "Injecting libc/libstdc++"
24
INJECT_LIBC=true
25
shift
26
fi
27
28
if [ "$#" -ne 5 ]; then
29
echo "Syntax: $0 [-inject-libc] <duckstation-qt|duckstation-mini> <target arch> <path to build directory> <deps prefix> <chroot dir>"
30
exit 1
31
fi
32
33
BINARY=$1
34
ARCH=$2
35
BUILDDIR=$3
36
DEPSDIR=$4
37
CHROOTDIR=$5
38
39
STRIP=llvm-strip
40
41
if [ "$ARCH" == "arm64" ]; then
42
DEBARCH="arm64"
43
RUNTIMEARCH="aarch64"
44
TRIPLE="aarch64-linux-gnu"
45
elif [ "$ARCH" == "armhf" ]; then
46
DEBARCH="armhf"
47
RUNTIMEARCH="armhf"
48
TRIPLE="arm-linux-gnueabihf"
49
else
50
echo "Unknown arch ${ARCH}."
51
exit 1
52
fi
53
54
if [ "$BINARY" == "duckstation-qt" ]; then
55
echo "Building Qt AppImage."
56
ADD_QT_LIBS=true
57
APPIMAGENAME="DuckStation-${ARCH}"
58
elif [ "$BINARY" == "duckstation-mini" ]; then
59
echo "Building Mini AppImage."
60
ADD_QT_LIBS=false
61
APPIMAGENAME="DuckStation-Mini-${ARCH}"
62
else
63
echo "Unknown binary $BINARY"
64
exit 1
65
fi
66
67
APPDIRNAME="${APPIMAGENAME}.AppDir"
68
69
declare -a SYSLIBS=(
70
"libbrotlicommon.so.1"
71
"libbrotlidec.so.1"
72
"libbsd.so.0"
73
"libcom_err.so.2"
74
"libcrypto.so.3"
75
"libcurl.so.4"
76
"libffi.so.8"
77
"libgnutls.so.30"
78
"libgssapi_krb5.so.2"
79
"libhogweed.so.6"
80
"libidn2.so.0"
81
"libk5crypto.so.3"
82
"libkeyutils.so.1"
83
"libkrb5.so.3"
84
"libkrb5support.so.0"
85
"liblber-2.5.so.0"
86
"libldap-2.5.so.0"
87
"libnettle.so.8"
88
"libnghttp2.so.14"
89
"libp11-kit.so.0"
90
"libpsl.so.5"
91
"librtmp.so.1"
92
"libsasl2.so.2"
93
"libssh.so.4"
94
"libssl.so.3"
95
"libtasn1.so.6"
96
"libudev.so.1"
97
"libunistring.so.2"
98
)
99
100
declare -a DEPLIBS=(
101
"libbacktrace.so.0"
102
"libfreetype.so.6"
103
"libharfbuzz.so"
104
"libjpeg.so.62"
105
"libpng16.so.16"
106
"libSDL3.so.0"
107
"libsharpyuv.so.0"
108
"libwebpdemux.so.2"
109
"libwebpmux.so.3"
110
"libwebp.so.7"
111
"libzip.so.5"
112
"libzstd.so.1"
113
114
"libcpuinfo.so"
115
"libdiscord-rpc.so"
116
"libplutosvg.so.0"
117
"libshaderc_ds.so"
118
"libsoundtouch.so.2"
119
"libspirv-cross-c-shared.so.0"
120
121
#"libavcodec.so.61"
122
#"libavformat.so.61"
123
#"libavutil.so.59"
124
#"libswscale.so.8"
125
#"libswresample.so.5"
126
#"libva-drm.so.2"
127
#"libva.so.2"
128
)
129
130
if [ "${ADD_QT_LIBS}" == true ]; then
131
SYSLIBS+=(
132
"libatk-1.0.so.0"
133
"libatk-bridge-2.0.so.0"
134
"libatspi.so.0"
135
"libblkid.so.1"
136
"libcairo-gobject.so.2"
137
"libcairo.so.2"
138
"libcap.so.2"
139
"libdatrie.so.1"
140
"libdeflate.so.0"
141
"libdbus-1.so.3"
142
"libepoxy.so.0"
143
"libgcrypt.so.20"
144
"libgdk-3.so.0"
145
"libgdk_pixbuf-2.0.so.0"
146
"libgio-2.0.so.0"
147
"libglib-2.0.so.0"
148
"libgmodule-2.0.so.0"
149
"libgobject-2.0.so.0"
150
"libgraphite2.so.3"
151
"libgtk-3.so.0"
152
"libjbig.so.0"
153
"liblz4.so.1"
154
"liblzma.so.5"
155
"libmd.so.0"
156
"libmount.so.1"
157
"libpango-1.0.so.0"
158
"libpangocairo-1.0.so.0"
159
"libpangoft2-1.0.so.0"
160
"libpcre2-16.so.0"
161
"libpcre2-8.so.0"
162
"libpcre.so.3"
163
"libpixman-1.so.0"
164
"libselinux.so.1"
165
"libsystemd.so.0"
166
"libtiff.so.5"
167
"libXau.so.6"
168
"libxcb-cursor.so.0"
169
"libxcb-glx.so.0"
170
"libxcb-icccm.so.4"
171
"libxcb-image.so.0"
172
"libxcb-keysyms.so.1"
173
"libxcb-randr.so.0"
174
"libxcb-render.so.0"
175
"libxcb-render-util.so.0"
176
"libxcb-shape.so.0"
177
"libxcb-shm.so.0"
178
"libxcb-sync.so.1"
179
"libxcb-util.so.1"
180
"libxcb-xfixes.so.0"
181
"libxcb-xkb.so.1"
182
"libXcomposite.so.1"
183
"libXcursor.so.1"
184
"libXdamage.so.1"
185
"libXdmcp.so.6"
186
"libXext.so.6"
187
"libXfixes.so.3"
188
"libXinerama.so.1"
189
"libXi.so.6"
190
"libxkbcommon.so.0"
191
"libxkbcommon-x11.so.0"
192
"libXrandr.so.2"
193
"libXrender.so.1"
194
)
195
196
declare -a QTLIBS=(
197
"libQt6Core.so.6"
198
"libQt6DBus.so.6"
199
"libQt6Gui.so.6"
200
"libQt6OpenGL.so.6"
201
"libQt6Svg.so.6"
202
"libQt6WaylandClient.so.6"
203
"libQt6WaylandEglClientHwIntegration.so.6"
204
"libQt6Widgets.so.6"
205
"libQt6XcbQpa.so.6"
206
)
207
208
declare -a QTPLUGINS=(
209
"plugins/iconengines"
210
"plugins/imageformats"
211
"plugins/platforminputcontexts"
212
"plugins/platforms"
213
"plugins/platformthemes"
214
"plugins/wayland-decoration-client"
215
"plugins/wayland-graphics-integration-client"
216
"plugins/wayland-shell-integration"
217
"plugins/xcbglintegrations"
218
)
219
fi
220
221
set -e
222
IFS="
223
"
224
225
APPIMAGETOOL=./appimagetool-x86_64
226
APPIMAGERUNTIME=./runtime-${RUNTIMEARCH}
227
PATCHELF=patchelf
228
229
if [ ! -f "$APPIMAGETOOL" ]; then
230
retry_command wget -O "$APPIMAGETOOL" https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/linux/appimagetool-x86_64.AppImage
231
chmod +x "$APPIMAGETOOL"
232
fi
233
234
if [ ! -f "$APPIMAGERUNTIME" ]; then
235
retry_command wget -O "$APPIMAGERUNTIME" https://github.com/stenzek/type2-runtime/releases/download/continuous/runtime-${RUNTIMEARCH}
236
fi
237
238
OUTDIR=$(realpath "./$APPDIRNAME")
239
rm -fr "$OUTDIR"
240
mkdir "$OUTDIR"
241
mkdir -p "$OUTDIR/usr/bin" "$OUTDIR/usr/lib"
242
243
echo "Copying binary and resources..."
244
cp -a "$BUILDDIR/bin/$BINARY" "$BUILDDIR/bin/resources" "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin"
245
246
# Patch RPATH so the binary goes hunting for shared libraries in the AppDir instead of system.
247
echo "Patching RPATH in ${BINARY}..."
248
patchelf --set-rpath '$ORIGIN/../lib' "$OUTDIR/usr/bin/$BINARY"
249
250
# Libraries we pull in from the system.
251
echo "Copying system libraries..."
252
for lib in "${SYSLIBS[@]}"; do
253
blib=$(basename "$lib")
254
echo "$CHROOTDIR/lib/$TRIPLE/$lib"
255
if [ -f "$CHROOTDIR/lib/$TRIPLE/$lib" ]; then
256
cp "$CHROOTDIR/lib/$TRIPLE/$lib" "$OUTDIR/usr/lib/$blib"
257
elif [ -f "$CHROOTDIR/usr/lib/$TRIPLE/$lib" ]; then
258
cp "$CHROOTDIR/usr/lib/$TRIPLE/$lib" "$OUTDIR/usr/lib/$blib"
259
elif [ -f "$CHROOTDIR/lib/$lib" ]; then
260
cp "$CHROOTDIR/lib/$lib" "$OUTDIR/usr/lib/$blib"
261
elif [ -f "$CHROOTDIR/usr/lib/$lib" ]; then
262
cp "$CHROOTDIR/usr/lib/$lib" "$OUTDIR/usr/lib/$blib"
263
else
264
echo "*** Failed to find '$blib'"
265
exit 1
266
fi
267
268
"$STRIP" $OUTDIR/usr/lib/$blib
269
done
270
271
echo "Copying local libraries..."
272
for lib in "${DEPLIBS[@]}"; do
273
blib=$(basename "$lib")
274
echo "$DEPSDIR/lib/$lib"
275
if [ -f "$DEPSDIR/lib/$lib" ]; then
276
cp "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib/$blib"
277
else
278
echo "*** Failed to find '$blib'"
279
exit 1
280
fi
281
282
"$STRIP" "$OUTDIR/usr/lib/$blib"
283
done
284
285
if [ "${ADD_QT_LIBS}" == true ]; then
286
echo "Copying Qt libraries..."
287
for lib in "${QTLIBS[@]}"; do
288
cp -avL "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib"
289
"$STRIP" "$OUTDIR/usr/lib/$lib"
290
done
291
292
echo "Copying Qt plugins..."
293
mkdir -p $OUTDIR/usr/lib/plugins
294
for plugin in "${QTPLUGINS[@]}"; do
295
mkdir -p "$OUTDIR/usr/lib/$plugin"
296
cp -avL "$DEPSDIR/$plugin/"*.so "$OUTDIR/usr/lib/$plugin/"
297
done
298
299
for so in $(find $OUTDIR/usr/lib/plugins -iname '*.so'); do
300
# This is ../../ because it's usually plugins/group/name.so
301
echo "Patching RPATH in ${so}..."
302
patchelf --set-rpath '$ORIGIN/../..' "$so"
303
"$STRIP" "$so"
304
done
305
306
echo "Creating qt.conf..."
307
cat > "$OUTDIR/usr/bin/qt.conf" << EOF
308
[Paths]
309
Plugins = ../lib/plugins
310
EOF
311
312
fi
313
314
for so in $(find $OUTDIR/usr/lib -maxdepth 1); do
315
if [ -f "$so" ]; then
316
echo "Patching RPATH in ${so}"
317
patchelf --set-rpath '$ORIGIN' "$so"
318
fi
319
done
320
321
# Copy desktop/icon
322
echo "Copying desktop/icon..."
323
mkdir -p "$OUTDIR/usr/share/applications"
324
mkdir -p "$OUTDIR/usr/share/icons/hicolor/512x512/apps"
325
cp -v "$SCRIPTDIR/../org.duckstation.DuckStation.desktop" "$OUTDIR/usr/share/applications"
326
cp -v "$SCRIPTDIR/../org.duckstation.DuckStation.png" "$OUTDIR/usr/share/icons/hicolor/512x512/apps"
327
ln -s "usr/share/applications/org.duckstation.DuckStation.desktop" "$OUTDIR"
328
ln -s "usr/share/icons/hicolor/512x512/apps/org.duckstation.DuckStation.png" "$OUTDIR"
329
330
# Generate AppStream meta-info.
331
echo "Generating AppStream metainfo..."
332
mkdir -p "$OUTDIR/usr/share/metainfo"
333
"$SCRIPTDIR/../generate-metainfo.sh" "$OUTDIR/usr/share/metainfo"
334
335
# Copy AppRun
336
cp "$SCRIPTDIR/apprun-cross.sh" "$OUTDIR/AppRun"
337
chmod +x "$OUTDIR/AppRun"
338
ln -s "usr/bin/$BINARY" "$OUTDIR/AppRun.wrapped"
339
340
# Optionally inject libc
341
if [ "$INJECT_LIBC" == true ]; then
342
echo "Injecting libc/libc++..."
343
"$SCRIPTDIR/inject-libc.sh" "$OUTDIR" "$DEBARCH" "$TRIPLE" "$BINARY"
344
fi
345
346
echo "Generating AppImage..."
347
rm -f "$APPIMAGENAME.AppImage"
348
349
# Can't run the appimage in a docker container because no fuse, so extract it first.
350
"$APPIMAGETOOL" --appimage-extract
351
"$PWD/squashfs-root/AppRun" -v --runtime-file "$APPIMAGERUNTIME" "$OUTDIR" "$APPIMAGENAME.AppImage"
352
353