-
Notifications
You must be signed in to change notification settings - Fork 2
/
pelf-dwfs
executable file
·540 lines (473 loc) · 17.9 KB
/
pelf-dwfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
#!/bin/sh
# Create a temporary directory for bundling the files
_VAR_WORKDIR="/tmp/pelf_$(date '+%s%M%S')"
_VAR_TO_BE_BUNDLED="$_VAR_WORKDIR/blob"
mkdir -p "$_VAR_TO_BE_BUNDLED" || exit 1
# Trap all termination signals
trap 'rm -rf "$_VAR_WORKDIR"' INT TERM HUP QUIT EXIT
#
# Exit with error message
error() {
printf >&2 "ERROR: %s\n" "$*"
exit 1
}
# Log to STDERR
log() {
printf >&2 "%s\n" "$*"
}
# Function to display usage/help information
print_help() {
cat <<EOF
Usage: $0 [options]
Options:
--output-to=[filepath.dwfs.AppBundle] Specify the output file name for the bundle.
--compression=[MKDWARFS_COMP_OPTS] Specify compression flags for mkdwarfs.
--add-appdir=[directory.AppDir] Add an AppDir.
--appbundle-id=[AppBundleID] Specify the ID of the AppBundle.
--embed-static-tools Embed static tools into the bundle.
--static-tools-dir=[directory] Specify a custom directory from which to get the static tools.
--retrieve-runtime=[filepath] Output the runtime to a file and exit
--no-upx Disables usage of UPX compression in the static tools.
--help Display this help and exit.
Examples:
$0 --add-appdir=/path/to/appdir --appbundle-id=myapp-date-myInternetNickName --output-to=mybundle.AppBundle --compression="--max-lookback-blocks=5 --no-history --compression zstd:level=22"
Notes:
- pelf-dwfs is a utility for creating self-contained DWFS bundles.
- For more information, visit https://github.com/xplshn/pelf
EOF
}
# Function to handle file/directory existence checks and operations
check_and_process() {
FPATH="$1"
ACTION="$2"
if [ -e "$FPATH" ]; then
eval "$ACTION" "$FPATH"
else
error "$FPATH does not exist."
fi
}
# Parse command-line arguments using getopt
if ! ARGS=$(getopt -o "" --long output-to:,compression:,add-appdir:,appbundle-id:,static-tools-dir:,retrieve-runtime:,embed-static-tools,no-upx,help -n "$0" -- "$@"); then
print_help
exit 1
fi
# Evaluate the parsed arguments
eval set -- "$ARGS"
# Initialize variables
_VAR_APPDIR="" _VAR_APPBUNDLE_ID="" _VAR_OUT_FILE="" _VAR_COMPRESS_ARG="" EMBED_STATIC_TOOLS=0 _VAR_CUSTOM_EMBED_DIR="" _VAR_RETRIEVE_RUNTIME="" _VAR_NOUPX=0
# Process options
while true; do
case "$1" in
--help)
print_help
exit 0
;;
--output-to)
_VAR_OUT_FILE="$2"
shift 2
;;
--compression)
_VAR_COMPRESS_ARG="$2"
shift 2
;;
--add-appdir)
_VAR_APPDIR="$2"
shift 2
;;
--appbundle-id)
_VAR_APPBUNDLE_ID="$2"
shift 2
;;
--embed-static-tools)
EMBED_STATIC_TOOLS="1"
shift
;;
--static-tools-dir)
_VAR_CUSTOM_EMBED_DIR="$2"
shift 2
;;
--retrieve-runtime)
_VAR_RETRIEVE_RUNTIME="$2"
shift 2
;;
--no-upx)
_VAR_NOUPX="1"
shift
;;
--)
shift
break
;;
*)
echo "Unexpected option: $1" >&2
print_help
exit 1
;;
esac
done
# Check for required arguments after parsing
if [ -z "$_VAR_RETRIEVE_RUNTIME" ]; then
if [ -z "$_VAR_APPDIR" ] || [ -z "$_VAR_APPBUNDLE_ID" ] || [ -z "$_VAR_OUT_FILE" ]; then
echo "Error: Required arguments not provided."
echo "You must specify:"
echo " --add-appdir <AppDir>"
echo " --appbundle-id <PROGRAM-DATE-MAINTAINER>"
echo " --output-to <OUTPUT.AppBundle>"
print_help
exit 1
fi
fi
[ -z "$_VAR_RETRIEVE_RUNTIME" ] && {
# Check if the APPDIR exists and is a directory
if [ -d "$_VAR_APPDIR" ]; then
# Check for the existence of the AppRun file
if [ ! -f "$_VAR_APPDIR/AppRun" ]; then
echo "This AppDir does not contain an AppRun" >&2
# Attempt to copy a generic AppRun if it exists
if [ -f "./AppRun.generic" ]; then
echo "Copying a generic AppRun" >&2
cp ./AppRun.generic "$_VAR_APPDIR/AppRun"
else
echo "Downloading a generic AppRun..." >&2
wget -q -O "$_VAR_APPDIR/AppRun" "https://raw.githubusercontent.com/xplshn/pelf/refs/heads/pelf-ng/assets/AppRun.generic" || {
echo "Failed to download AppRun." >&2
exit 1
}
fi
chmod +x "$_VAR_APPDIR/AppRun"
fi
# Process the directory and copy items
check_and_process "$_VAR_APPDIR" "true" &&
find "$_VAR_APPDIR" -mindepth 1 -maxdepth 1 | while IFS= read -r item; do
cp -dR "$item" "${_VAR_TO_BE_BUNDLED}/"
done
else
echo "[$_VAR_APPDIR] is not an existing directory" >&2
exit 1
fi
# Throw an error if any of these are missing
for cmd in mkdwarfs dwarfs fusermount3; do
if ! command -v "$cmd" >/dev/null 2>&1; then
error "$cmd could not be found. Please install it and try again"
fi
done
# Function to handle file/directory existence checks and operations
check_and_process() {
FPATH="$1"
ACTION="$2"
if [ -e "$FPATH" ]; then
eval "$ACTION" "$FPATH"
else
echo "$FPATH does not exist." >&2
exit 1
fi
}
[ "$EMBED_STATIC_TOOLS" != 1 ] && [ "$_VAR_CUSTOM_EMBED_DIR" != "" ] && {
echo "Unable to specify static-tools-dir without passing --embed-static-tools"
exit 55
}
# Create a DWFS archive of the executable, libraries and additional files
[ -n "$_VAR_COMPRESS_ARG" ] || _VAR_COMPRESS_ARG="-l7 -C zstd:level=22 --metadata-compression null -S 21 -B 8 --order nilsimsa -W 12 -w 4" # --max-lookback-blocks=5 --block-size-bits=18 --compression zstd:level=22 # --max-lookback-blocks=5 --no-history --categorize=pcmaudio --compression pcmaudio/waveform::flac:level=8:exhaustive #_VAR_COMPRESS_ARG="--max-lookback-blocks=5 --categorize=pcmaudio --compression pcmaudio/waveform::flac:level=8"
# shellcheck disable=SC2086
if ! mkdwarfs --set-owner 0 --set-group 0 --no-create-timestamp --no-history --input "$_VAR_TO_BE_BUNDLED" --output "$_VAR_WORKDIR/archive.dwfs" --progress=ascii $_VAR_COMPRESS_ARG; then
error "Compression failed"
fi
}
# Create a self-extracting archive
_VAR_HOST_INFO="$(uname -mrspv)"
CONST_VERSION="1.9_dwfs_raw" # (vanilla)
# Generate the loader script with conditional LD_LIBRARY_PATH replacements
LOADER_SCRIPT=$(
sed -e "s|__ENTRY_POINT__|${_VAR_APPBUNDLE_ID##*/}|g" \
-e "s|__PELF_VERSION__|${CONST_VERSION}|g" \
-e "s|__PELF_HOST__|${_VAR_HOST_INFO}|g" <<'_END_OF_LOADER_SCRIPT'
#!/bin/sh
# This file was automatically generated by __PELF_VERSION__. Find out more about it here: https://github.com/xplshn/pelf
# Please DO NOT EDIT this file, unless you are testing for changes/fixes. If you found the need to modify
# PELF's behavior, submit a PR of your "PELF EDITION" if you think it is appropiate. -
# NOTE: The only way to make changes to this file is by using a `vi` implementation like Busybox's, which respects non-visible characters
# shellcheck disable=SC3028 # 18,74 # In POSIX sh, RANDOM is undefined. [SC3028] # That's ok, we also use epoch date
# shellcheck disable=SC2317 # 107,9 # Command appears to be unreachable. Check usage (or *ignore* if invoked indirectly) # We call this function via a `trap`
# Get the binary's name
[ -n "$EXE_NAME" ] || EXE_NAME="__ENTRY_POINT__"
# rEXE_NAME can be used as a variable name, and it is also used for creating the work dir
rEXE_NAME="$(echo "$EXE_NAME" | tr -dc '[:alnum:]_')"
# Important variables
_VAR_POOL_DIR="${TMPDIR:-/tmp}/.pelfbundles" # Temporary pool directory
_VAR_BWORK_DIR="${_VAR_POOL_DIR}/pbundle_${rEXE_NAME}$(date '+%s%M%S')_${RANDOM}" # Temporary work directory
_VAR_MOUNT_DIR="$_VAR_BWORK_DIR/mounted" # Temporary directory for mounting
_VAR_EXEC_FILE="${_VAR_MOUNT_DIR}/AppRun" # The file to execute
SELF="$(realpath "$0")" # Path to the .AppBundle itself
mkdir -p "$_VAR_BWORK_DIR"
# Nice variables
__RED="\033[0;31m"
__YELLOW="\033[0;33m"
__RESET="\033[0m"
# Functions that are called when things aren't going well
log() {
printf >&2 "AppBundle Runtime ${__YELLOW}Warning${__RESET}: %s\n" "$*"
}
error() {
printf >&2 "AppBundle Runtime ${__RED}Error${__RESET}: %s\n" "$*"
exit 1
}
# Check if dwarfs is available
check_fuse() {
# Check if dwarfs AND fusermount3 is available in the system PATH
if command -v dwarfs >/dev/null && command -v fusermount3 >/dev/null; then
return
fi
# If either dwarfs or fusermount is not available, check if they are bundled in the script
__STATIC_TOOLS_ARCHIVE_MARKER=$(awk '/^__STATIC_TOOLS__/ { print NR + 1; exit }' "$0") || error "Failed to locate static tools marker in the script."
if [ -z "$__STATIC_TOOLS_ARCHIVE_MARKER" ]; then
error "Failed to locate static tools marker in the script. No static tools where bundled and your system lacks dwarfs"
fi
_VAR_STATIC_TOOLS_DIR="$_VAR_BWORK_DIR/static/$(uname -om | tr ' ' '_')"
# Ensure the directory for extracted tools exists
mkdir -p "$_VAR_STATIC_TOOLS_DIR" || error "Failed to create directory $_VAR_STATIC_TOOLS_DIR"
# Extract the bundled tar archive
tail -n +"$__STATIC_TOOLS_ARCHIVE_MARKER" "$0" | base64 -d 2>/dev/null | tar -xzf - -C "$_VAR_STATIC_TOOLS_DIR" || error "Failed to extract tar archive."
# Add the extracted tools to PATH
PATH="$PATH:$_VAR_STATIC_TOOLS_DIR" || error "Failed to update PATH with the extracted tools."
# Check again if either dwarfs or fusermount is available
if command -v dwarfs >/dev/null || command -v fusermount3 >/dev/null; then
return
fi
error "Neither dwarfs nor fusermount3 are available in the system and were not found in the bundled archive."
}
# Mount the DwarFS archive from an offset in the script
mount_dwarfs() {
check_fuse
# Mount the embedded DwarFS archive using the offset
mkdir -p "$_VAR_MOUNT_DIR" && {
dwarfs -o offset="auto",ro,auto_unmount "$SELF" "$_VAR_MOUNT_DIR" >"${_VAR_BWORK_DIR}/.dwarfs.log" 2>&1 || {
log "ERROR: Failed to mount DwarFS archive."
log "W: DEBUG INFO:"
log " \$0 is set to $0"
log " The realpath to \$0 is $SELF"
exit 1
}
}
}
determine_home() {
setEnvIfExists() {
dirSuffix="$1"
envVar="$2"
oldEnvVar="$3"
wouldbeVar="$4"
dir="$SELF$dirSuffix"
if [ -d "$dir" ]; then
# Check if the old environment variable is empty and set it
oldValue=$(eval "echo \$$oldEnvVar")
if [ -z "$oldValue" ]; then
envValue=$(eval "echo \$$envVar")
export "$oldEnvVar=$envValue"
fi
eval export "$envVar=\"$dir\""
else
eval export "$wouldbeVar=\"$dir\""
fi
}
setEnvIfExists ".home" "HOME" "OLD_HOME" "WOULDBE_HOME"
if [ ! -d "${SELF}.home" ]; then
setEnvIfExists ".config" "XDG_CONFIG_HOME" "OLD_XDG_CONFIG_HOME" "WOULDBE_CONFIG"
fi
}
determine_home
# Main function to handle the logic
main() {
mount_dwarfs && touch "${_VAR_BWORK_DIR}/.$$" # Creats a hidden file in the work dir of the instance of this AppBundle, to allow for scripts to determine if the runtime is still running. This is a feature that some people may want if they wish to write wild AppRuns/external scripts
cleanup() {
(
# Attempt to unmount
fusermount3 -u "$_VAR_MOUNT_DIR" 2>/dev/null
# Wait and check if the mount point is unmounted
for i in 1 2 3 4 5; do
if mountpoint -q "$_VAR_MOUNT_DIR"; then
sleep "$i"
else
break
fi
done
# Force unmount if still mounted
if mountpoint -q "$_VAR_MOUNT_DIR"; then
fusermount3 -uz "$_VAR_MOUNT_DIR" 2>/dev/null
fi
# Remove temporary directories
rm -rf "$_VAR_BWORK_DIR" 2>/dev/null || true
rm -rf "$_VAR_POOL_DIR" 2>/dev/null || true
) & # Run cleanup in the background
}
# Set up cleanup trap
trap 'cleanup' INT TERM HUP QUIT EXIT
_BIN_DIRs="$_VAR_MOUNT_DIR/bin:$_VAR_MOUNT_DIR/usr/bin:$_VAR_MOUNT_DIR/shared/bin"
_LIB_DIRs="$_VAR_MOUNT_DIR/lib:$_VAR_MOUNT_DIR/usr/lib:$_VAR_MOUNT_DIR/shared/lib:$_VAR_MOUNT_DIR/lib64:$_VAR_MOUNT_DIR/usr/lib64:$_VAR_MOUNT_DIR/lib32:$_VAR_MOUNT_DIR/usr/lib32:$_VAR_MOUNT_DIR/libx32:$_VAR_MOUNT_DIR/usr/libx32"
# Add extra binaries to the PATH if they exist
if [ "$_VAR_FOUND_RUNNING_INSTANCE" != "1" ]; then
# Export directories with transformed variable names
export "${rEXE_NAME}_binDir=$_BIN_DIRs"
export "${rEXE_NAME}_libDir=$_LIB_DIRs"
export "${rEXE_NAME}_mountDir=$_VAR_MOUNT_DIR"
fi
# Figure out what we do
case "$1" in
--pbundle_help)
printf "This bundle was generated automatically by PELF __PELF_VERSION__, the machine on which it was created has the following \"uname -mrspv\":\n %s \n" "__PELF_HOST__"
printf "Usage:\n <|--pbundle_help|--pbundle_list|--pbundle_link <binary>|--pbundle_pngIcon|--pbundle_svgIcon|--pbundle_desktop|--pbundle_appstream|--pbundle_portableHome|--pbundle_portableConfig|> <args...>\n"
exit 1
;;
--pbundle_list)
find "$_VAR_MOUNT_DIR" && exit 0
exit 1
;;
--pbundle_link)
# NOTE: This would be under execute_file, except LD_LIBRARY_PATH MUSTN'T be set when using an AppRun, but we do need this var in order to run a binary manually
if [ "$PELF_NO_LD_VAR" != "1" ]; then
if [ -z "$LD_LIBRARY_PATH" ]; then
LD_LIBRARY_PATH="$_LIB_DIRs"
else
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$_LIB_DIRs"
fi
export LD_LIBRARY_PATH
fi
_VAR_EXEC_FILE="$2"
shift 2
;;
--pbundle_pngIcon)
if [ -f "$_VAR_MOUNT_DIR/.DirIcon" ]; then
base64 "$_VAR_MOUNT_DIR/.DirIcon"
exit 0
else
exit 1
fi
;;
--pbundle_svgIcon)
if [ -f "$_VAR_MOUNT_DIR/.DirIcon.svg" ]; then
base64 "$_VAR_MOUNT_DIR/.DirIcon.svg"
exit 0
else
exit 1
fi
;;
--pbundle_desktop)
for file in "$_VAR_MOUNT_DIR"/*.desktop; do
if [ -e "$file" ]; then
base64 "$file"
exit 0
fi
done
exit 1
;;
--pbundle_appstream)
for file in "$_VAR_MOUNT_DIR"/*.xml; do
if [ -e "$file" ]; then
base64 "$file"
exit 0
fi
done
exit 1
;;
--pbundle_portableHome)
if [ -z "$WOULDBE_HOME" ] && [ -n "$WOULDBE_CONFIG" ] && [ -d "$WOULDBE_CONFIG" ]; then
error "You cannot have a portable .home & .config of this AppBundle"
fi
mkdir -p "$WOULDBE_HOME"
determine_home
shift
;;
--pbundle_portableConfig)
if [ -z "$WOULDBE_CONFIG" ] && [ -n "$WOULDBE_HOME" ] && [ -d "$WOULDBE_HOME" ]; then
error "You cannot have a portable .config & .home of this AppBundle"
fi
mkdir -p "$WOULDBE_CONFIG"
determine_home
shift
;;
esac
# Execute the specified file from the mounted directory
execute_file() {
# Add directories to that do exists to the xPATH
[ -d "$_VAR_MOUNT_DIR/bin" ] && xPATH="${xPATH:+$xPATH:}$_VAR_MOUNT_DIR/bin"
[ -d "$_VAR_MOUNT_DIR/usr/bin" ] && xPATH="$_VAR_MOUNT_DIR/usr/bin"
# Update PATH based on PBUNDLE_OVERTAKE_PATH
if [ "$PBUNDLE_OVERTAKE_PATH" = 1 ]; then
PATH="$xPATH${PATH:+:$PATH}"
else
PATH="${PATH:+$PATH:}$xPATH"
fi
export PATH
# Append to XDG_DATA_DIRS if it exists and we have ./share || ./usr/share directories
if [ -n "$XDG_DATA_DIRS" ]; then
[ -d "$_VAR_MOUNT_DIR/share" ] && XDG_DATA_DIRS="$XDG_DATA_DIRS:$_VAR_MOUNT_DIR/share"
[ -d "$_VAR_MOUNT_DIR/usr/share" ] && XDG_DATA_DIRS="$XDG_DATA_DIRS:$_VAR_MOUNT_DIR/usr/share"
fi
export XDG_DATA_DIRS
# Utility variables for scripting/AppRuns/wrappers
export SELF_TEMPDIR="$_VAR_MOUNT_DIR"
export SELF
export ARGV0="${0##*/}" # Lots of AppDirs for AppImages expect this, it should containthe basename of "$0"
if ! command -v "$_VAR_EXEC_FILE" >/dev/null 2>&1; then
error "[$2] does NOT exist. It is not contained here nor is it available in the user's PATH"
fi
# Proceed to execute the packed ENTRY POINT binary
"$_VAR_EXEC_FILE" "$@"
}
execute_file "$@"
}
main "$@"
exit $?
_END_OF_LOADER_SCRIPT
)
# Create RUNTIME
[ -z "$_VAR_RETRIEVE_RUNTIME" ] || {
echo "$LOADER_SCRIPT" >"$_VAR_RETRIEVE_RUNTIME"
log "Successfully wrote exported the runtime to \"$_VAR_RETRIEVE_RUNTIME\""
log " Notes:
1. Add the marker for the static tools tgz prior to embedding the dwarfs archive. Remember to convert the tgz archive to base64 and only after adding the marker add the base64 static.tgz archive to this runtime
2. After that, add the dwarfs archive to the runtime (this file)
3. Then mark as executable and you're good to go"
exit 0
} && {
echo "$LOADER_SCRIPT" >"$_VAR_OUT_FILE"
}
embed_static_tools() {
_VAR_STATIC_TOOLS_DIR="$_VAR_WORKDIR/static/$(uname -om | tr ' ' '_')"
# Create the static tools directory
mkdir -p "$_VAR_STATIC_TOOLS_DIR" || error "Failed to create directory $_VAR_WORKDIR/static"
# Check if _VAR_CUSTOM_EMBED_DIR is set and use it; otherwise, use `which` to find the tools
if [ -n "$_VAR_CUSTOM_EMBED_DIR" ]; then
# Ensure the custom embed directory exists
if [ ! -d "$_VAR_CUSTOM_EMBED_DIR" ]; then
error "Custom embed directory $_VAR_CUSTOM_EMBED_DIR does not exist"
fi
# Copy the tools from the custom embed directory
if ! cp "$_VAR_CUSTOM_EMBED_DIR/fusermount3" "$_VAR_CUSTOM_EMBED_DIR/dwarfs" "$_VAR_STATIC_TOOLS_DIR"; then
error "Failed to copy static tools from custom embed directory"
fi
else
# Copy static tools using `which`
if ! cp "$(which fusermount3)" "$(which dwarfs)" "$_VAR_STATIC_TOOLS_DIR"; then
error "Failed to copy static tools"
fi
fi
if [ -z "$_VAR_NOUPX" ]; then
if command -v "upx" >/dev/null 2>&1; then
for file in "$_VAR_STATIC_TOOLS_DIR"/*; do
upx "$file" >/dev/null 2>&1
done
fi
fi
# Create the tar archive
if ! tar -C "$_VAR_STATIC_TOOLS_DIR" -czf "$_VAR_WORKDIR/static.tgz" .; then
error "Compression of static tools failed"
fi
# Append the marker and the tar archive to the output file
printf "\n__STATIC_TOOLS__\n" >>"$_VAR_OUT_FILE" || error "Failed to write marker (TAR) to output file"
base64 <"$_VAR_WORKDIR/static.tgz" >>"$_VAR_OUT_FILE" || error "Failed to append TAR archive to output file"
}
[ -n "$EMBED_STATIC_TOOLS" ] && embed_static_tools
# Append the DWFS archive to the self-extracting script
printf "\n__ARCHIVE_MARKER__\n" >>"$_VAR_OUT_FILE" || error "Failed to write marker (DWFS) to output file"
cat <"$_VAR_WORKDIR/archive.dwfs" >>"$_VAR_OUT_FILE" || error "Failed to append DWFS archive to output file"
# Make the self-extracting script executable
chmod +x "$_VAR_OUT_FILE" || error "Could not mark output file as executable"