Skip to content

Commit 607073c

Browse files
feat(clean): clean old Xcode DeviceSupport versions instead of just their caches (#519)
1 parent 6770778 commit 607073c

File tree

1 file changed

+101
-5
lines changed

1 file changed

+101
-5
lines changed

lib/clean/dev.sh

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,101 @@ clean_xcode_documentation_cache() {
339339
fi
340340
}
341341

342+
# Clean old Xcode DeviceSupport versions, keeping the most recent ones.
343+
# Each version holds debug symbols (1-3 GB) for a specific iOS/watchOS/tvOS version.
344+
# Symbols regenerate automatically when a device running that version is connected.
345+
# Args: $1=directory path, $2=display name (e.g. "iOS DeviceSupport")
346+
clean_xcode_device_support() {
347+
local ds_dir="$1"
348+
local display_name="$2"
349+
local keep_count="${MOLE_XCODE_DEVICE_SUPPORT_KEEP:-2}"
350+
[[ "$keep_count" =~ ^[0-9]+$ ]] || keep_count=2
351+
352+
[[ -d "$ds_dir" ]] || return 0
353+
354+
# Collect version directories (each is a platform version like "17.5 (21F79)")
355+
local -a version_dirs=()
356+
while IFS= read -r -d '' entry; do
357+
# Skip non-directories (e.g. .log files at the top level)
358+
[[ -d "$entry" ]] || continue
359+
version_dirs+=("$entry")
360+
done < <(command find "$ds_dir" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
361+
362+
if [[ ${#version_dirs[@]} -le $keep_count ]]; then
363+
# Nothing to remove, still clean caches/logs inside existing versions
364+
safe_clean "$ds_dir"/*/Symbols/System/Library/Caches/* "$display_name symbol cache"
365+
safe_clean "$ds_dir"/*.log "$display_name logs"
366+
return 0
367+
fi
368+
369+
# Sort by modification time (most recent first)
370+
local -a sorted_dirs=()
371+
while IFS= read -r line; do
372+
sorted_dirs+=("${line#* }")
373+
done < <(
374+
for entry in "${version_dirs[@]}"; do
375+
local mtime
376+
mtime=$(stat -f%m "$entry" 2> /dev/null || echo "0")
377+
printf '%s %s\n' "$mtime" "$entry"
378+
done | sort -rn
379+
)
380+
381+
# Split into keep vs remove
382+
local -a stale_dirs=()
383+
local idx=0
384+
for entry in "${sorted_dirs[@]}"; do
385+
if [[ $idx -lt $keep_count ]]; then
386+
((idx++)) || true
387+
continue
388+
fi
389+
stale_dirs+=("$entry")
390+
((idx++)) || true
391+
done
392+
393+
if [[ ${#stale_dirs[@]} -eq 0 ]]; then
394+
safe_clean "$ds_dir"/*/Symbols/System/Library/Caches/* "$display_name symbol cache"
395+
safe_clean "$ds_dir"/*.log "$display_name logs"
396+
return 0
397+
fi
398+
399+
# Calculate total size of stale versions
400+
local stale_size_kb=0
401+
local stale_entry
402+
for stale_entry in "${stale_dirs[@]}"; do
403+
local entry_size_kb
404+
entry_size_kb=$(get_path_size_kb "$stale_entry" 2> /dev/null || echo "0")
405+
stale_size_kb=$((stale_size_kb + entry_size_kb))
406+
done
407+
local stale_size_human
408+
stale_size_human=$(bytes_to_human "$((stale_size_kb * 1024))")
409+
410+
if [[ "$DRY_RUN" == "true" ]]; then
411+
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} ${display_name} · would remove ${#stale_dirs[@]} old versions (${stale_size_human}), keeping ${keep_count} most recent"
412+
note_activity
413+
return 0
414+
fi
415+
416+
# Remove old versions
417+
local removed_count=0
418+
for stale_entry in "${stale_dirs[@]}"; do
419+
if should_protect_path "$stale_entry" || is_path_whitelisted "$stale_entry"; then
420+
continue
421+
fi
422+
if safe_remove "$stale_entry"; then
423+
removed_count=$((removed_count + 1))
424+
fi
425+
done
426+
427+
if [[ $removed_count -gt 0 ]]; then
428+
echo -e " ${GREEN}${ICON_SUCCESS}${NC} ${display_name} · removed ${removed_count} old versions, ${stale_size_human}"
429+
note_activity
430+
fi
431+
432+
# Still clean caches/logs inside the kept versions
433+
safe_clean "$ds_dir"/*/Symbols/System/Library/Caches/* "$display_name symbol cache"
434+
safe_clean "$ds_dir"/*.log "$display_name logs"
435+
}
436+
342437
_sim_runtime_mount_points() {
343438
if [[ -n "${MOLE_XCODE_SIM_RUNTIME_MOUNT_POINTS:-}" ]]; then
344439
printf '%s\n' "$MOLE_XCODE_SIM_RUNTIME_MOUNT_POINTS"
@@ -697,11 +792,12 @@ clean_dev_mobile() {
697792
fi
698793
note_activity
699794
fi
700-
# DeviceSupport caches/logs (preserve core support files).
701-
safe_clean ~/Library/Developer/Xcode/iOS\ DeviceSupport/*/Symbols/System/Library/Caches/* "iOS device symbol cache"
702-
safe_clean ~/Library/Developer/Xcode/iOS\ DeviceSupport/*.log "iOS device support logs"
703-
safe_clean ~/Library/Developer/Xcode/watchOS\ DeviceSupport/*/Symbols/System/Library/Caches/* "watchOS device symbol cache"
704-
safe_clean ~/Library/Developer/Xcode/tvOS\ DeviceSupport/*/Symbols/System/Library/Caches/* "tvOS device symbol cache"
795+
# Old iOS/watchOS/tvOS DeviceSupport versions (debug symbols for connected devices).
796+
# Each iOS version creates a 1-3 GB folder of debug symbols. Only the versions
797+
# matching currently used devices are needed; older ones regenerate on device connect.
798+
clean_xcode_device_support ~/Library/Developer/Xcode/iOS\ DeviceSupport "iOS DeviceSupport"
799+
clean_xcode_device_support ~/Library/Developer/Xcode/watchOS\ DeviceSupport "watchOS DeviceSupport"
800+
clean_xcode_device_support ~/Library/Developer/Xcode/tvOS\ DeviceSupport "tvOS DeviceSupport"
705801
# Simulator runtime caches.
706802
safe_clean ~/Library/Developer/CoreSimulator/Profiles/Runtimes/*/Contents/Resources/RuntimeRoot/System/Library/Caches/* "Simulator runtime cache"
707803
safe_clean ~/Library/Caches/Google/AndroidStudio*/* "Android Studio cache"

0 commit comments

Comments
 (0)