55
66# Usage info
77usage () {
8- echo " Usage: $0 [cpu|cu121|cu118|cu124|cu129|...]"
8+ echo " Usage: $0 [cpu|cu121|cu118|cu124|cu129|...] [--macos-app] "
99 echo " cpu - Install CPU-only PyTorch (default)"
1010 echo " cuXXX - Install CUDA-enabled PyTorch (e.g., cu121 for CUDA 12.1)"
11+ echo " --macos-app - Create macOS .app bundle (macOS only)"
1112 exit 1
1213}
1314
14- # Parse argument, default to "cpu" if not provided
15+ # Parse arguments
1516TORCH_VARIANT=" ${1:- cpu} "
17+ MACOS_APP=false
1618
17- # Set PyInstaller mode based on torch variant
18- if [[ " $TORCH_VARIANT " == cpu ]]; then
19+ # Check for --macos-app flag
20+ for arg in " $@ " ; do
21+ case $arg in
22+ --macos-app)
23+ MACOS_APP=true
24+ shift
25+ ;;
26+ esac
27+ done
28+
29+ # Validate macOS app option
30+ if [[ " $MACOS_APP " == true && " $( uname) " != " Darwin" ]]; then
31+ echo " Error: --macos-app option can only be used on macOS"
32+ exit 1
33+ fi
34+
35+ # Set PyInstaller mode based on torch variant and platform
36+ if [[ " $MACOS_APP " == true ]]; then
37+ PYINSTALLER_MODE=" --windowed"
38+ elif [[ " $TORCH_VARIANT " == cpu ]]; then
1939 PYINSTALLER_MODE=" --onefile"
2040else
2141 PYINSTALLER_MODE=" --onedir"
@@ -36,6 +56,10 @@ case "$TORCH_VARIANT" in
3656 ;;
3757esac
3858
59+ # After installing PyTorch
60+ pip cache purge
61+ python -c " import torch; print(f'PyTorch cache cleared')"
62+
3963# Make sure build tools and hooks are up to date
4064python -m pip install -U pip wheel setuptools
4165python -m pip install -U pyinstaller pyinstaller-hooks-contrib
@@ -49,37 +73,82 @@ pip install .
4973echo " Installing CLIP model..."
5074python -c " import clip; clip.load('ViT-B/32')"
5175
76+ # Prepare PyInstaller arguments
77+ PYINSTALLER_ARGS=(
78+ --hidden-import clip
79+ --hidden-import numpy
80+ --hidden-import torch
81+ --hidden-import torchvision
82+ --hidden-import photomap
83+ --hidden-import photomap.backend
84+ --hidden-import photomap.backend.photomap_server
85+ --hidden-import photomap.backend.main_wrapper
86+ --hidden-import photomap.backend.routers
87+ --hidden-import photomap.backend.routers.album
88+ --hidden-import photomap.backend.routers.search
89+ --hidden-import photomap.backend.embeddings
90+ --hidden-import photomap.backend.config
91+ --hidden-import uvicorn
92+ --hidden-import fastapi
93+ --collect-all torch
94+ --collect-all torchvision
95+ --collect-all clip
96+ --collect-all numpy
97+ --collect-all sklearn
98+ --collect-all PIL
99+ --collect-all photomap
100+ --add-data " $( python -c " import clip; print(clip.__path__[0])" ) :clip"
101+ --add-data " $HOME /.cache/clip:clip_models"
102+ --add-data " photomap/frontend/static:photomap/frontend/static"
103+ --add-data " photomap/frontend/templates:photomap/frontend/templates"
104+ --paths .
105+ $PYINSTALLER_MODE
106+ --argv-emulation
107+ --name photomap
108+ -y
109+ )
110+
111+ # Add macOS-specific options if building app bundle
112+ if [[ " $MACOS_APP " == true ]]; then
113+ PYINSTALLER_ARGS+=(
114+ --osx-bundle-identifier org.4crabs.photomap
115+ --icon photomap/frontend/static/icons/icon.icns
116+ )
117+ echo " Building macOS .app bundle..."
118+ else
119+ echo " Building standard executable..."
120+ fi
121+
52122# Run PyInstaller
53- pyinstaller \
54- --hidden-import clip \
55- --hidden-import numpy \
56- --hidden-import torch \
57- --hidden-import torchvision \
58- --hidden-import photomap \
59- --hidden-import photomap.backend \
60- --hidden-import photomap.backend.photomap_server \
61- --hidden-import photomap.backend.main_wrapper \
62- --hidden-import photomap.backend.routers \
63- --hidden-import photomap.backend.routers.album \
64- --hidden-import photomap.backend.routers.search \
65- --hidden-import photomap.backend.embeddings \
66- --hidden-import photomap.backend.config \
67- --hidden-import uvicorn \
68- --hidden-import fastapi \
69- --collect-all torch \
70- --collect-all torchvision \
71- --collect-all clip \
72- --collect-all numpy \
73- --collect-all sklearn \
74- --collect-all PIL \
75- --collect-all photomap \
76- --add-data " $( python -c " import clip; print(clip.__path__[0])" ) :clip" \
77- --add-data " $HOME /.cache/clip:clip_models" \
78- --add-data " photomap/frontend/static:photomap/frontend/static" \
79- --add-data " photomap/frontend/templates:photomap/frontend/templates" \
80- --paths . \
81- $PYINSTALLER_MODE \
82- --argv-emulation \
83- --name photomap \
84- -y \
85- photomap/backend/photomap_server.py
123+ pyinstaller " ${PYINSTALLER_ARGS[@]} " photomap/backend/photomap_server.py
124+
125+ # Before running PyInstaller
126+ echo " Disk space before PyInstaller:"
127+ df -h
128+
129+ # After PyInstaller
130+ rm -rf build/ # Remove PyInstaller temp files
131+
132+ # Post-process macOS .app bundle to launch in Terminal
133+ if [[ " $MACOS_APP " == true ]]; then
134+ APP_BUNDLE=" dist/photomap.app"
135+ MACOS_DIR=" $APP_BUNDLE /Contents/MacOS"
136+ BIN_NAME=" photomap"
137+
138+ # Create a launcher script
139+ LAUNCHER=" $MACOS_DIR /run_in_terminal.sh"
140+ cat > " $LAUNCHER " << EOF
141+ #!/bin/bash
142+ exec osascript -e 'tell application "Terminal" to do script "'"$MACOS_DIR /$BIN_NAME "'"'
143+ EOF
144+ chmod +x " $LAUNCHER "
145+
146+ # Update Info.plist to use the launcher script
147+ PLIST=" $APP_BUNDLE /Contents/Info.plist"
148+ /usr/libexec/PlistBuddy -c " Set :CFBundleExecutable run_in_terminal.sh" " $PLIST "
149+
150+ echo " ✅ macOS app bundle created: dist/photomap.app"
151+ echo " Users can double-click photomap.app to launch PhotoMap in Terminal"
152+ else
153+ echo " ✅ Executable created in dist/ directory"
154+ fi
0 commit comments