Repository navigation
Expand file tree
/
Copy pathcreate-appimage-interactive.sh
More file actions
396 lines (328 loc) · 12.5 KB
/
Copy pathcreate-appimage-interactive.sh
File metadata and controls
396 lines (328 loc) · 12.5 KB
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
#!/bin/bash
################################################################################
# AppImage Creator Script (Interactive)
#
# Enhanced version with robust interactive selection
#
# Usage: ./create-appimage-interactive.sh <app-folder> [app-name] [version]
################################################################################
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
show_usage() {
cat << EOF
Usage: $0 <app-folder> [app-name] [version]
Arguments:
app-folder Path to the folder containing your compiled application
app-name Name of the application (optional, will auto-detect)
version Version number (optional, default: 1.0)
Examples:
$0 ./myapp
$0 ./myapp MyApplication 2.1
$0 /path/to/app "My Cool App" 1.0
EOF
}
if [ $# -lt 1 ]; then
show_usage
exit 1
fi
APP_FOLDER="$1"
APP_NAME="${2:-}"
VERSION="${3:-1.0}"
if [ ! -d "$APP_FOLDER" ]; then
print_error "Application folder '$APP_FOLDER' does not exist!"
exit 1
fi
APP_FOLDER=$(realpath "$APP_FOLDER")
print_info "Application folder: $APP_FOLDER"
# Function to detect actual executable files
detect_executables() {
local search_path="$1"
local found_execs=()
while IFS= read -r -d '' file; do
if [[ "$file" =~ \.so($|\.) ]] || [[ "$file" =~ \.a$ ]]; then
continue
fi
if file "$file" | grep -q "ELF.*executable"; then
found_execs+=("$file")
elif file "$file" | grep -q "script"; then
found_execs+=("$file")
fi
done < <(find "$search_path" -maxdepth 3 -type f -executable -print0 2>/dev/null)
printf '%s\n' "${found_execs[@]}"
}
# Function to get user selection (more robust)
get_user_selection() {
local max=$1
local selection=""
# Try reading from terminal directly
if [ -t 0 ]; then
# stdin is a terminal
exec 3<&0 # Save stdin
read -r selection
exec 0<&3 # Restore stdin
exec 3<&- # Close fd 3
else
# Try to read from /dev/tty
if [ -e /dev/tty ]; then
read -r selection < /dev/tty
else
print_error "Cannot read from terminal!"
print_error "Please run this script interactively or specify the executable name:"
print_error " $0 $APP_FOLDER <executable-name>"
exit 1
fi
fi
echo "$selection"
}
# Auto-detect app name if not provided
if [ -z "$APP_NAME" ]; then
print_info "Auto-detecting application executable..."
mapfile -t EXECUTABLES < <(detect_executables "$APP_FOLDER")
if [ ${#EXECUTABLES[@]} -eq 0 ]; then
print_error "No executable found in $APP_FOLDER"
print_error ""
print_error "Troubleshooting:"
print_error " 1. Make sure your executable has execute permissions:"
print_error " chmod +x $APP_FOLDER/your-app"
print_error " 2. Or specify the app name manually:"
print_error " $0 $APP_FOLDER your-app-name"
exit 1
elif [ ${#EXECUTABLES[@]} -eq 1 ]; then
MAIN_EXECUTABLE="${EXECUTABLES[0]}"
APP_NAME=$(basename "$MAIN_EXECUTABLE")
print_success "Found executable: $APP_NAME"
else
# Multiple executables found
echo ""
print_warning "Multiple executables found in $APP_FOLDER"
echo ""
echo "Please select the main executable:"
echo ""
for i in "${!EXECUTABLES[@]}"; do
exec_name=$(basename "${EXECUTABLES[$i]}")
exec_path=$(realpath --relative-to="$APP_FOLDER" "${EXECUTABLES[$i]}")
exec_type=$(file "${EXECUTABLES[$i]}" | cut -d: -f2 | cut -d, -f1 | xargs)
printf " ${GREEN}[%d]${NC} %-20s ${BLUE}(%s)${NC}\n" "$((i+1))" "$exec_name" "$exec_type"
printf " Path: %s\n" "$exec_path"
done
echo ""
echo -n "Enter selection [1-${#EXECUTABLES[@]}]: "
# Get selection
selection=$(get_user_selection ${#EXECUTABLES[@]})
# Validate
if [ -z "$selection" ]; then
print_error "No selection made!"
exit 1
fi
if ! [[ "$selection" =~ ^[0-9]+$ ]]; then
print_error "Invalid input: '$selection'"
print_error "Please enter a number between 1 and ${#EXECUTABLES[@]}"
exit 1
fi
if [ "$selection" -lt 1 ] || [ "$selection" -gt ${#EXECUTABLES[@]} ]; then
print_error "Invalid selection: $selection"
print_error "Please enter a number between 1 and ${#EXECUTABLES[@]}"
exit 1
fi
MAIN_EXECUTABLE="${EXECUTABLES[$((selection-1))]}"
APP_NAME=$(basename "$MAIN_EXECUTABLE")
echo ""
print_success "Selected: $APP_NAME"
fi
else
print_info "Looking for executable named '$APP_NAME'..."
MAIN_EXECUTABLE=$(find "$APP_FOLDER" -maxdepth 3 -type f -name "$APP_NAME" -executable 2>/dev/null | head -n 1)
if [ -z "$MAIN_EXECUTABLE" ]; then
mapfile -t ALL_EXECUTABLES < <(detect_executables "$APP_FOLDER")
for exec in "${ALL_EXECUTABLES[@]}"; do
if [ "$(basename "$exec")" = "$APP_NAME" ]; then
MAIN_EXECUTABLE="$exec"
break
fi
done
fi
if [ -z "$MAIN_EXECUTABLE" ]; then
print_error "Could not find executable named '$APP_NAME' in $APP_FOLDER"
echo ""
print_info "Available executables:"
mapfile -t AVAILABLE < <(detect_executables "$APP_FOLDER")
if [ ${#AVAILABLE[@]} -eq 0 ]; then
echo " (none found)"
else
for exec in "${AVAILABLE[@]}"; do
echo " - $(basename "$exec")"
done
fi
exit 1
fi
print_success "Found executable: $MAIN_EXECUTABLE"
fi
# Verify executable
if [ ! -x "$MAIN_EXECUTABLE" ]; then
print_error "File exists but is not executable: $MAIN_EXECUTABLE"
print_error "Fix with: chmod +x $MAIN_EXECUTABLE"
exit 1
fi
EXEC_TYPE=$(file "$MAIN_EXECUTABLE" | cut -d: -f2 | xargs)
print_info "Executable type: $EXEC_TYPE"
# Create AppDir
WORK_DIR=$(mktemp -d)
APPDIR="$WORK_DIR/${APP_NAME}.AppDir"
print_info "Creating AppDir structure in $APPDIR..."
mkdir -p "$APPDIR"
mkdir -p "$APPDIR/usr/bin"
mkdir -p "$APPDIR/usr/lib"
mkdir -p "$APPDIR/usr/share/applications"
mkdir -p "$APPDIR/usr/share/icons/hicolor/256x256/apps"
# Copy application files
print_info "Copying application files while preserving structure..."
# Get the relative path of the executable from the app folder
EXEC_NAME=$(basename "$MAIN_EXECUTABLE")
EXEC_REL_PATH=$(realpath --relative-to="$APP_FOLDER" "$MAIN_EXECUTABLE")
# Copy all files preserving structure
cp -r "$APP_FOLDER"/* "$APPDIR/usr/bin/" 2>/dev/null || {
print_warning "Some files could not be copied, continuing..."
}
# Copy hidden files if they exist
if ls -A "$APP_FOLDER"/.[!.]* >/dev/null 2>&1; then
cp -r "$APP_FOLDER"/.[!.]* "$APPDIR/usr/bin/" 2>/dev/null || true
fi
# Copy main executable to AppDir root
if [ -f "$APPDIR/usr/bin/$EXEC_REL_PATH" ]; then
cp "$APPDIR/usr/bin/$EXEC_REL_PATH" "$APPDIR/$EXEC_NAME"
else
print_error "Could not find executable after copying: $EXEC_REL_PATH"
exit 1
fi
print_success "Application files copied with original structure preserved"
print_info "Executable location: usr/bin/$EXEC_REL_PATH"
# Detect and copy dependencies
print_info "Detecting dependencies..."
get_dependencies() {
local binary="$1"
ldd "$binary" 2>/dev/null | grep "=> /" | awk '{print $3}' | grep -v "^/lib" | grep -v "^/lib64" | sort -u
}
DEPS=$(get_dependencies "$MAIN_EXECUTABLE")
if [ -n "$DEPS" ]; then
print_info "Bundling dependencies..."
while IFS= read -r dep; do
if [ -f "$dep" ]; then
lib_name=$(basename "$dep")
if [[ ! "$dep" =~ ^/lib ]] && [[ ! "$dep" =~ ^/usr/lib ]]; then
print_info " Copying: $lib_name"
cp -L "$dep" "$APPDIR/usr/lib/" 2>/dev/null || true
fi
fi
done <<< "$DEPS"
fi
find "$APP_FOLDER" -name "*.so*" -type f -exec cp {} "$APPDIR/usr/lib/" \; 2>/dev/null || true
# Create AppRun script
print_info "Creating AppRun launcher..."
cat > "$APPDIR/AppRun" << 'APPRUN_EOF'
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
export PATH="${HERE}/usr/bin:${PATH}"
export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS}"
export APPIMAGE_SILENT_INSTALL=1
cd "${HERE}/usr/bin" || exit 1
exec "${HERE}/usr/bin/EXEC_PATH_PLACEHOLDER" "$@"
APPRUN_EOF
sed -i "s|EXEC_PATH_PLACEHOLDER|$EXEC_REL_PATH|g" "$APPDIR/AppRun"
chmod +x "$APPDIR/AppRun"
print_info "AppRun configured to execute: usr/bin/$EXEC_REL_PATH"
# Create .desktop file
print_info "Creating .desktop file..."
cat > "$APPDIR/${APP_NAME}.desktop" << EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Exec=$EXEC_NAME %F
Icon=$APP_NAME
Categories=Utility;Application;
Terminal=false
StartupNotify=true
StartupWMClass=$APP_NAME
Comment=$APP_NAME Application
X-AppImage-Name=$APP_NAME
X-AppImage-Version=$VERSION
EOF
cp "$APPDIR/${APP_NAME}.desktop" "$APPDIR/usr/share/applications/"
print_success "Desktop file created"
# Create minimal icon
print_info "Setting up application icon..."
# Create base minimal icon
echo "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" | base64 -d > "$APPDIR/${APP_NAME}.png"
# Look for user's icon
USER_ICON=$(find "$APP_FOLDER" -type f \( -iname "*.png" -o -iname "*.svg" -o -iname "*icon*" -o -iname "*logo*" \) 2>/dev/null | head -n 1)
if [ -f "$USER_ICON" ]; then
print_info "Found icon: $USER_ICON"
ICON_EXT="${USER_ICON##*.}"
if [ "$ICON_EXT" = "png" ]; then
cp "$USER_ICON" "$APPDIR/${APP_NAME}.png"
elif [ "$ICON_EXT" = "svg" ]; then
if command -v convert &> /dev/null; then
print_info "Converting SVG to PNG..."
convert -background none "$USER_ICON" -resize 256x256 "$APPDIR/${APP_NAME}.png" 2>/dev/null || cp "$USER_ICON" "$APPDIR/${APP_NAME}.png"
elif command -v rsvg-convert &> /dev/null; then
rsvg-convert -w 256 -h 256 "$USER_ICON" -o "$APPDIR/${APP_NAME}.png" 2>/dev/null || cp "$USER_ICON" "$APPDIR/${APP_NAME}.png"
else
print_warning "No SVG converter found, using fallback"
fi
elif command -v convert &> /dev/null; then
convert "$USER_ICON" -resize 256x256 "$APPDIR/${APP_NAME}.png" 2>/dev/null || true
fi
fi
# Install to all required locations
print_info "Installing icon to all standard locations..."
# .DirIcon for AppImage
cp "$APPDIR/${APP_NAME}.png" "$APPDIR/.DirIcon" 2>/dev/null || true
# All standard sizes for hicolor theme
for size in 16 22 24 32 48 64 128 256 512; do
icon_dir="$APPDIR/usr/share/icons/hicolor/${size}x${size}/apps"
mkdir -p "$icon_dir"
if command -v convert &> /dev/null; then
convert "$APPDIR/${APP_NAME}.png" -resize ${size}x${size} "$icon_dir/${APP_NAME}.png" 2>/dev/null || cp "$APPDIR/${APP_NAME}.png" "$icon_dir/${APP_NAME}.png"
else
cp "$APPDIR/${APP_NAME}.png" "$icon_dir/${APP_NAME}.png" 2>/dev/null || true
fi
done
# Pixmaps for older systems
mkdir -p "$APPDIR/usr/share/pixmaps"
cp "$APPDIR/${APP_NAME}.png" "$APPDIR/usr/share/pixmaps/${APP_NAME}.png" 2>/dev/null || true
print_success "Icon installation complete"
# Download appimagetool
APPIMAGETOOL="appimagetool-x86_64.AppImage"
if [ ! -f "$APPIMAGETOOL" ]; then
print_info "Downloading appimagetool..."
wget -q --show-progress "https://github.com/AppImage/AppImageKit/releases/download/continuous/$APPIMAGETOOL"
chmod +x "$APPIMAGETOOL"
fi
# Build AppImage
print_info "Building AppImage..."
OUTPUT_NAME="${APP_NAME}-${VERSION}-x86_64.AppImage"
export NO_APPSTREAM=1
ARCH=x86_64 ./"$APPIMAGETOOL" --no-appstream "$APPDIR" "$OUTPUT_NAME" 2>&1 | grep -i "error" || true
if [ -f "$OUTPUT_NAME" ]; then
chmod +x "$OUTPUT_NAME"
print_success "AppImage created successfully!"
print_success "Output: $(realpath "$OUTPUT_NAME")"
SIZE=$(du -h "$OUTPUT_NAME" | cut -f1)
print_info "Size: $SIZE"
echo ""
print_info "Run with: ./$OUTPUT_NAME"
else
print_error "Failed to create AppImage!"
exit 1
fi
rm -rf "$WORK_DIR"
print_success "Done!"