#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
SRC_DIR="${PROJECT_ROOT}/src"
ENTRY_POINT="${SRC_DIR}/witty_mcp_manager/__main__.py"
MODE="onefile"
OUTPUT_DIR="${PROJECT_ROOT}/dist"
CLEAN_BUILD=0
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
MODE="$2"
shift 2
;;
--output-dir)
OUTPUT_DIR="$2"
shift 2
;;
--clean)
CLEAN_BUILD=1
shift
;;
-h|--help)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --mode MODE Build mode: onefile (default) or standalone"
echo " --output-dir DIR Output directory (default: ./dist)"
echo " --clean Clean build directories before building"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
if [[ "$MODE" != "onefile" && "$MODE" != "standalone" ]]; then
echo "Error: Invalid mode '$MODE'. Use 'onefile' or 'standalone'." >&2
exit 1
fi
command -v python3 >/dev/null 2>&1 || { echo "Error: python3 is required" >&2; exit 1; }
command -v gcc >/dev/null 2>&1 || command -v clang >/dev/null 2>&1 || { echo "Error: C compiler (gcc/clang) is required" >&2; exit 1; }
if ! python3 -c "import nuitka" 2>/dev/null; then
echo "Error: Nuitka is not installed. Run: pip install nuitka ordered-set" >&2
exit 1
fi
if [[ "$(uname -s)" == "Linux" ]]; then
command -v patchelf >/dev/null 2>&1 || { echo "Error: patchelf is required on Linux" >&2; exit 1; }
fi
echo "=============================================="
echo "Witty MCP Manager - Nuitka Build"
echo "=============================================="
echo "Mode: ${MODE}"
echo "Output: ${OUTPUT_DIR}"
echo "Entry: ${ENTRY_POINT}"
echo "Python: $(python3 --version)"
echo "Nuitka: $(python3 -m nuitka --version | head -1)"
echo "=============================================="
if [[ "$CLEAN_BUILD" -eq 1 ]]; then
echo "[1/4] Cleaning previous build artifacts..."
rm -rf "${OUTPUT_DIR}"/__main__.build \
"${OUTPUT_DIR}"/__main__.dist \
"${OUTPUT_DIR}"/__main__.onefile-build \
"${OUTPUT_DIR}"/witty-mcp
else
echo "[1/4] Skipping clean (use --clean to force)"
fi
mkdir -p "${OUTPUT_DIR}"
echo "[2/4] Ensuring dependencies are available..."
cd "${PROJECT_ROOT}"
if [[ -f "uv.lock" ]] && command -v uv >/dev/null 2>&1; then
uv sync --quiet 2>/dev/null || true
fi
echo "[3/4] Building with Nuitka (mode=${MODE})..."
NUITKA_OPTS=(
"--mode=${MODE}"
"--output-dir=${OUTPUT_DIR}"
"--output-filename=witty-mcp"
"--include-package=witty_mcp_manager"
"--noinclude-pytest-mode=nofollow"
"--noinclude-setuptools-mode=nofollow"
"--noinclude-custom-mode=setuptools:nofollow"
"--noinclude-custom-mode=pip:nofollow"
"--noinclude-custom-mode=wheel:nofollow"
"--noinclude-custom-mode=distutils:nofollow"
"--noinclude-custom-mode=pkg_resources:nofollow"
"--python-flag=no_docstrings"
"--python-flag=-OO"
"--python-flag=no_asserts"
"--nowarn-mnemonic=not-given-as-argument"
)
if [[ "$(uname -s)" == "Linux" ]]; then
NUITKA_OPTS+=("--static-libpython=auto")
fi
PYTHONPATH="${SRC_DIR}:${PYTHONPATH:-}" python3 -m nuitka "${NUITKA_OPTS[@]}" "${ENTRY_POINT}"
echo "[4/4] Verifying build..."
BINARY="${OUTPUT_DIR}/witty-mcp"
if [[ ! -f "$BINARY" ]]; then
echo "Error: Build failed - binary not found at ${BINARY}" >&2
exit 1
fi
SIZE=$(du -h "${BINARY}" | cut -f1)
FILE_TYPE=$(file "${BINARY}" | cut -d: -f2)
echo ""
echo "=============================================="
echo "Build successful!"
echo "=============================================="
echo "Binary: ${BINARY}"
echo "Size: ${SIZE}"
echo "Type: ${FILE_TYPE}"
echo ""
echo "Test with:"
echo " ${BINARY} version"
echo " ${BINARY} --help"
echo "=============================================="