-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: complete compilation including homebrew formulae generation
- Loading branch information
1 parent
4c98108
commit d6f2b31
Showing
4 changed files
with
284 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
cask "exo" do | ||
version "0.1.0" | ||
sha256 "57fc9b838688a4dbd4842db4a96888f7627d5df16fd633bf2401340a7388cba6" | ||
|
||
url "http://localhost:8000/exo-0.1.0-darwin-arm64.zip" | ||
name "Exo" | ||
desc "MLX-powered AI assistant" | ||
homepage "https://github.com/exo-explorer/exo" | ||
|
||
depends_on macos: ">= :ventura" | ||
depends_on arch: :arm64 | ||
|
||
binary "#{staged_path}/exo-0.1.0-darwin-arm64/exo" | ||
|
||
postflight do | ||
set_permissions "#{staged_path}/exo-0.1.0-darwin-arm64/exo", "0755" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
# Configuration | ||
VERSION="0.1.0" | ||
APP_NAME="exo" | ||
DIST_DIR="dist" | ||
PACKAGE_NAME="${APP_NAME}-${VERSION}-darwin-arm64" | ||
|
||
# 1. Clean previous builds | ||
echo "Cleaning previous builds..." | ||
rm -rf build dist | ||
|
||
# 2. Run PyInstaller | ||
echo "Building with PyInstaller..." | ||
pyinstaller exo.spec | ||
|
||
# 3. Create a clean distribution directory | ||
echo "Creating distribution package..." | ||
mkdir -p "${DIST_DIR}/${PACKAGE_NAME}" | ||
cp -r "dist/${APP_NAME}/"* "${DIST_DIR}/${PACKAGE_NAME}/" | ||
|
||
# 4. Create ZIP file | ||
echo "Creating ZIP archive..." | ||
cd "${DIST_DIR}" | ||
zip -r "${PACKAGE_NAME}.zip" "${PACKAGE_NAME}" | ||
cd .. | ||
|
||
# 5. Calculate SHA256 | ||
echo "Calculating SHA256..." | ||
SHA256=$(shasum -a 256 "${DIST_DIR}/${PACKAGE_NAME}.zip" | cut -d' ' -f1) | ||
|
||
# 6. Generate Homebrew Cask formula | ||
echo "Generating Homebrew formula..." | ||
cat > Formula/exo.rb << EOL | ||
cask "exo" do | ||
version "${VERSION}" | ||
sha256 "${SHA256}" | ||
url "https://github.com/sethburkart123/exo/releases/download/test/exo-0.1.0-darwin-arm64.zip" | ||
name "Exo" | ||
desc "MLX-powered AI assistant" | ||
homepage "https://github.com/exo-explorer/exo" | ||
depends_on macos: ">= :ventura" | ||
depends_on arch: :arm64 | ||
binary "#{staged_path}/exo-${VERSION}-darwin-arm64/exo" | ||
postflight do | ||
set_permissions "#{staged_path}/exo-${VERSION}-darwin-arm64/exo", "0755" | ||
end | ||
end | ||
EOL | ||
|
||
echo "Done! Package created at: ${DIST_DIR}/${PACKAGE_NAME}.zip" | ||
echo "SHA256: ${SHA256}" | ||
echo "" | ||
echo "Next steps:" | ||
echo "1. Upload ${PACKAGE_NAME}.zip to GitHub releases" | ||
echo "2. Update the URL in the formula with your actual GitHub repository" | ||
echo "3. Test the formula locally with: brew install --cask ./Formula/exo.rb" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
# -*- mode: python ; coding: utf-8 -*- | ||
import sys | ||
import os | ||
import shutil | ||
from PyInstaller.utils.hooks import collect_all, collect_submodules, copy_metadata | ||
|
||
# Basic Configuration | ||
block_cipher = None | ||
name = os.environ.get('EXO_NAME', 'exo') | ||
spec_dir = os.path.dirname(os.path.abspath(SPEC)) | ||
root_dir = os.path.abspath(os.path.join(spec_dir)) | ||
|
||
# Get Python library path dynamically | ||
python_exec = sys.executable | ||
python_prefix = sys.prefix | ||
if sys.platform.startswith('darwin'): | ||
# On macOS, construct library path based on current Python version | ||
version = f"{sys.version_info.major}.{sys.version_info.minor}" | ||
lib_patterns = [ | ||
os.path.join(python_prefix, 'lib', f'libpython{version}.dylib'), | ||
os.path.join(python_prefix, 'lib', f'libpython{version}m.dylib'), | ||
os.path.join(os.path.dirname(python_exec), '..', 'lib', f'libpython{version}.dylib'), | ||
] | ||
|
||
PYTHON_LIB = None | ||
for pattern in lib_patterns: | ||
if os.path.exists(pattern): | ||
PYTHON_LIB = pattern | ||
break | ||
|
||
if not PYTHON_LIB: | ||
raise FileNotFoundError(f"Could not find Python library for Python {version}") | ||
print(f"Using Python library: {PYTHON_LIB}") | ||
|
||
# Create a local copy of the Python library | ||
local_lib_dir = os.path.join(spec_dir, 'lib') | ||
os.makedirs(local_lib_dir, exist_ok=True) | ||
local_python_lib = os.path.join(local_lib_dir, 'libpython3.12.dylib') | ||
shutil.copy2(PYTHON_LIB, local_python_lib) | ||
print(f"Copied Python library to: {local_python_lib}") | ||
|
||
# Model Collection | ||
models_dir = os.path.join(root_dir, 'exo', 'inference', 'mlx', 'models') | ||
model_files = [] | ||
for root, dirs, files in os.walk(models_dir): | ||
for file in files: | ||
if file.endswith('.py') and file not in ['__init__.py', 'base.py']: | ||
model_files.append(os.path.join(root, file)) | ||
|
||
model_imports = [ | ||
f"exo.inference.mlx.models.{os.path.basename(f)[:-3]}" | ||
for f in model_files | ||
if '__pycache__' not in f | ||
] | ||
|
||
# Data Collection | ||
datas = [ | ||
(os.path.join(root_dir, 'exo/tinychat'), 'exo/tinychat'), | ||
(os.path.join(root_dir, 'exo'), 'exo'), | ||
(local_python_lib, '.'), | ||
] | ||
|
||
# Collect Transformers Data | ||
print("Collecting transformers data...") | ||
try: | ||
trans_datas, _, _ = collect_all('transformers') | ||
filtered_datas = [(src, dst) for src, dst in trans_datas | ||
if not any(x in dst.lower() for x in ['.git', 'test', 'examples'])] | ||
datas.extend(filtered_datas) | ||
datas.extend(copy_metadata('transformers')) | ||
except Exception as e: | ||
print(f"Warning: Could not collect transformers data: {e}") | ||
|
||
# MLX Integration | ||
if sys.platform.startswith('darwin'): | ||
print("Configuring macOS specific settings...") | ||
mlx_locations = [ | ||
'/opt/homebrew/Caskroom/miniconda/base/envs/exo/lib/python3.12/site-packages/mlx', | ||
os.path.join(root_dir, 'venv/lib/python3.12/site-packages/mlx'), | ||
os.path.join(python_prefix, 'lib/python3.12/site-packages/mlx'), # Added new search path | ||
] | ||
|
||
mlx_path = None | ||
for loc in mlx_locations: | ||
if os.path.exists(loc): | ||
mlx_path = os.path.abspath(loc) | ||
print(f"Found MLX at: {mlx_path}") | ||
break | ||
|
||
if mlx_path: | ||
datas.append((mlx_path, 'mlx')) | ||
# Search for metallib in multiple possible locations | ||
metallib_locations = [ | ||
os.path.join(mlx_path, 'backend/metal/kernels/mlx.metallib'), | ||
os.path.join(mlx_path, 'mlx/backend/metal/kernels/mlx.metallib'), | ||
os.path.join(python_prefix, 'lib/python3.12/site-packages/mlx/backend/metal/kernels/mlx.metallib'), | ||
] | ||
|
||
metallib_found = False | ||
for metallib_path in metallib_locations: | ||
if os.path.exists(metallib_path): | ||
print(f"Found metallib at: {metallib_path}") | ||
# Add metallib to both the root and the MLX directory structure | ||
datas.extend([ | ||
(metallib_path, '.'), | ||
(metallib_path, 'mlx/backend/metal/kernels') | ||
]) | ||
metallib_found = True | ||
break | ||
|
||
if not metallib_found: | ||
print("ERROR: Could not find mlx.metallib in any expected location!") | ||
print("Searched locations:", "\n".join(metallib_locations)) | ||
sys.exit(1) | ||
else: | ||
print("ERROR: MLX package not found in expected locations") | ||
sys.exit(1) | ||
|
||
# Initial binaries list with Python library | ||
binaries = [] | ||
if sys.platform.startswith('darwin'): | ||
binaries.append((local_python_lib, '.')) | ||
|
||
# Analysis Configuration | ||
a = Analysis( | ||
[os.path.join(root_dir, 'exo/main.py')], | ||
pathex=[root_dir], | ||
binaries=binaries, | ||
datas=datas, | ||
hiddenimports=[ | ||
'transformers', | ||
'safetensors', | ||
'safetensors.torch', | ||
'exo', | ||
'packaging.version', | ||
'packaging.specifiers', | ||
'packaging.requirements', | ||
'packaging.markers', | ||
'charset_normalizer', | ||
'requests', | ||
'urllib3', | ||
'certifi', | ||
'idna', | ||
'mlx', | ||
'mlx.core', | ||
'mlx.nn', | ||
'mlx.backend', | ||
'mlx.backend.metal', | ||
'mlx.backend.metal.kernels', | ||
'_sysconfigdata__darwin_darwin', | ||
] + model_imports, | ||
hookspath=[], | ||
hooksconfig={ | ||
'urllib3': {'ssl': True}, | ||
'transformers': {'module': True} | ||
}, | ||
runtime_hooks=[], | ||
excludes=[ | ||
'pytest', | ||
'sentry_sdk' | ||
], | ||
win_no_prefer_redirects=False, | ||
win_private_assemblies=False, | ||
cipher=block_cipher, | ||
noarchive=False, | ||
) | ||
|
||
# Make sure Python library is included in both datas and binaries | ||
a.datas = list(dict.fromkeys(a.datas + [(os.path.basename(local_python_lib), local_python_lib, 'DATA')])) | ||
a.binaries = list(dict.fromkeys(a.binaries + [(os.path.basename(local_python_lib), local_python_lib, 'BINARY')])) | ||
|
||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) | ||
|
||
exe = EXE( | ||
pyz, | ||
a.scripts, | ||
[], | ||
exclude_binaries=True, | ||
name=name, | ||
debug=False, # Enable debug mode | ||
bootloader_ignore_signals=False, | ||
strip=False, | ||
upx=True, | ||
console=True, | ||
disable_windowed_traceback=False, | ||
argv_emulation=False, | ||
target_arch='arm64', | ||
codesign_identity=None, | ||
entitlements_file=None, | ||
) | ||
# Create the collection | ||
coll = COLLECT( | ||
exe, | ||
a.binaries, | ||
a.zipfiles, | ||
a.datas, | ||
strip=False, | ||
upx=True, | ||
upx_exclude=[], | ||
name=name, | ||
) |