#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WEB_SRC_DIR="$ROOT_DIR/web/src" MAC_SRC_DIR="${MAC_SRC_DIR:-$ROOT_DIR/mac/src}" PYTHON_BIN="$ROOT_DIR/.venv/bin/python" LAUNCHER="$WEB_SRC_DIR/backend_embedded_launcher.py" BACKEND_BIN_NAME="${BACKEND_BIN_NAME:-WebBackend}" BUILD_ROOT="$ROOT_DIR/dist-backend-build" DIST_PATH="$BUILD_ROOT/dist" WORK_PATH="$BUILD_ROOT/build" SPEC_PATH="$BUILD_ROOT/spec" PROJECT_PATH="${MAC_PROJECT_PATH:-}" SCHEME="${MAC_SCHEME:-}" TARGET_DIR="${EMBEDDED_BACKEND_DIR:-}" discover_scheme() { local project_path="$1" python3 - "$project_path" <<'PY' import json import subprocess import sys project = sys.argv[1] try: output = subprocess.check_output( ["xcodebuild", "-list", "-project", project, "-json"], text=True, stderr=subprocess.DEVNULL, ) payload = json.loads(output) except Exception: print("") raise SystemExit(0) project_data = payload.get("project", {}) schemes = project_data.get("schemes") or [] print(schemes[0] if schemes else "") PY } if [[ ! -x "$PYTHON_BIN" ]]; then echo "Missing virtual environment. Run ./run.sh --setup-only first." >&2 exit 1 fi if [[ ! -f "$LAUNCHER" ]]; then echo "Missing launcher file: $LAUNCHER" >&2 exit 1 fi if [[ -z "$PROJECT_PATH" ]]; then PROJECT_PATH="$(find "$MAC_SRC_DIR" -maxdepth 4 -name "*.xcodeproj" | sort | head -n 1)" fi if [[ -z "$PROJECT_PATH" ]]; then echo "No .xcodeproj found under: $MAC_SRC_DIR" >&2 echo "Set MAC_PROJECT_PATH to the project you want to build." >&2 exit 1 fi PROJECT_DIR="$(dirname "$PROJECT_PATH")" if [[ -z "$SCHEME" ]]; then SCHEME="$(discover_scheme "$PROJECT_PATH")" fi if [[ -z "$SCHEME" ]]; then SCHEME="$(basename "$PROJECT_PATH" .xcodeproj)" fi if [[ -z "$TARGET_DIR" ]]; then DEFAULT_TARGET_DIR="$PROJECT_DIR/$SCHEME/EmbeddedBackend" if [[ -d "$DEFAULT_TARGET_DIR" ]]; then TARGET_DIR="$DEFAULT_TARGET_DIR" else TARGET_DIR="$(find "$PROJECT_DIR" -maxdepth 4 -type d -name EmbeddedBackend | sort | head -n 1)" fi fi if [[ -z "$TARGET_DIR" ]]; then TARGET_DIR="$PROJECT_DIR/$SCHEME/EmbeddedBackend" fi mkdir -p "$DIST_PATH" "$WORK_PATH" "$SPEC_PATH" "$TARGET_DIR" "$PYTHON_BIN" -m pip install -q pyinstaller PYI_ARGS=( --noconfirm --clean --onefile --name "$BACKEND_BIN_NAME" --distpath "$DIST_PATH" --workpath "$WORK_PATH" --specpath "$SPEC_PATH" --add-data "$WEB_SRC_DIR/app.py:." --collect-all streamlit --collect-all streamlit_autorefresh --hidden-import yfinance --hidden-import pandas --collect-all plotly --collect-all kaleido ) for source_dir in "$WEB_SRC_DIR"/*; do source_name="$(basename "$source_dir")" if [[ ! -d "$source_dir" ]]; then continue fi if [[ "$source_name" == "tests" ]] || [[ "$source_name" == "__pycache__" ]]; then continue fi PYI_ARGS+=(--add-data "$source_dir:$source_name") done if [[ -f "$WEB_SRC_DIR/ONBOARDING.md" ]]; then PYI_ARGS+=(--add-data "$WEB_SRC_DIR/ONBOARDING.md:.") fi "$PYTHON_BIN" -m PyInstaller "${PYI_ARGS[@]}" "$LAUNCHER" cp "$DIST_PATH/$BACKEND_BIN_NAME" "$TARGET_DIR/$BACKEND_BIN_NAME" chmod +x "$TARGET_DIR/$BACKEND_BIN_NAME" echo "Embedded backend updated: $TARGET_DIR/$BACKEND_BIN_NAME"