Files
fn_registry/.claude/commands/compile.md
T
egutierrez 157bc093f4 docs(compile): adopta layout local_files/ + enrichers/ + runtime/ Python
Actualiza /compile para que el deploy a Desktop/apps/<app>/ siga la
convencion local_files/ del framework:

- Copia .exe + ttfs + dlls junto al exe (read-only).
- Copia <app_dir>/enrichers/ si existe (excluyendo pycache).
- Copia <app_dir>/runtime/ si app.md declara python_runtime: true.
  Regenera el runtime via tools/freeze_python_runtime.sh windows
  cuando app.md es mas nuevo que runtime/.lock.
- NUNCA toca local_files/ del destino — contiene estado del
  usuario (DBs, ini, proyectos) que NO se debe perder al
  recompilar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:37:22 +02:00

7.2 KiB

/compile — Compila la app actual y la copia al escritorio de Windows

Compila una app del registry para los targets que soporte (Windows via MinGW, Android via Gradle/NDK si esta configurado) y deja el resultado en /mnt/c/Users/lucas/Desktop/apps/<app>/, listo para usar desde Windows.

Pensado para apps C++ del workspace cpp/ (donde ya hay toolchain mingw-w64.cmake y build dir cpp/build/windows/). Si en el futuro hay apps Android (Gradle wrapper o NDK), tambien las detecta.

Argumento

$ARGUMENTS — opcional. Nombre de la app a compilar (ej: chart_demo, registry_dashboard).

  • Sin argumento: detectar la app desde pwd (si estas dentro de cpp/apps/<X>/ o projects/*/apps/<X>/).
  • Si no hay app deducible y no se pasa argumento → listar apps disponibles y pedir nombre.
  • Si se pasa argumento, usarlo directamente.

Pasos

1. Resolver la app y su directorio fuente

ROOT=/home/lucas/fn_registry
APP_ARG="$ARGUMENTS"

# Detectar desde CWD si no hay argumento
if [ -z "$APP_ARG" ]; then
  CWD="$(pwd)"
  case "$CWD" in
    "$ROOT"/cpp/apps/*|"$ROOT"/projects/*/apps/*)
      APP_ARG="$(basename "$CWD")" ;;
  esac
fi

# Si sigue vacio, listar apps y abortar
if [ -z "$APP_ARG" ]; then
  echo "Apps disponibles:"
  ls "$ROOT"/cpp/apps/ 2>/dev/null
  ls "$ROOT"/projects/*/apps/ 2>/dev/null
  echo "Uso: /compile <app_name>"
  exit 1
fi

# Buscar el directorio real
APP_DIR=""
for cand in "$ROOT/cpp/apps/$APP_ARG" "$ROOT"/projects/*/apps/"$APP_ARG"; do
  [ -d "$cand" ] && APP_DIR="$cand" && break
done

if [ -z "$APP_DIR" ]; then
  echo "No encuentro app '$APP_ARG' en cpp/apps/ ni projects/*/apps/"
  exit 1
fi
echo "App: $APP_ARG"
echo "Dir: $APP_DIR"

2. Detectar targets soportados

Examinar el app.md y los archivos del directorio para decidir que se puede compilar:

  • Windows (MinGW): si la app tiene CMakeLists.txt y se registra en cpp/CMakeLists.txt (es decir, aparece como subdirectorio en cpp/build/windows/apps/<APP>/). Default para apps C++.
  • Android: si existe AndroidManifest.xml, build.gradle, build.gradle.kts o carpeta android/ dentro de $APP_DIR. (Hoy no hay ninguna; saltar silenciosamente.)
  • Linux (opcional, no por defecto): el build dir cpp/build/ ya genera el binario para Linux. Solo se hace si el usuario lo pide explicitamente.
TARGETS=()
[ -f "$APP_DIR/CMakeLists.txt" ] && TARGETS+=("windows")

if [ -f "$APP_DIR/AndroidManifest.xml" ] || \
   [ -f "$APP_DIR/build.gradle" ] || \
   [ -f "$APP_DIR/build.gradle.kts" ] || \
   [ -d "$APP_DIR/android" ]; then
  TARGETS+=("android")
fi

if [ ${#TARGETS[@]} -eq 0 ]; then
  echo "No se detecta ningun target compilable en $APP_DIR"
  exit 1
fi
echo "Targets: ${TARGETS[*]}"

3. Compilar Windows (cross-compile MinGW)

Solo si windows esta en TARGETS.

BUILD_WIN="$ROOT/cpp/build/windows"

# Configurar build dir si no existe
if [ ! -f "$BUILD_WIN/CMakeCache.txt" ]; then
  mkdir -p "$BUILD_WIN"
  cmake -S "$ROOT/cpp" -B "$BUILD_WIN" \
    -DCMAKE_TOOLCHAIN_FILE="$ROOT/cpp/toolchains/mingw-w64.cmake" \
    -DCMAKE_BUILD_TYPE=Release
fi

# Compilar SOLO el target de la app (no todo el arbol)
cmake --build "$BUILD_WIN" --target "$APP_ARG" -j"$(nproc)"

Si el target no existe en CMake (porque la app no esta registrada en cpp/CMakeLists.txt), reportar y proponer registrarla siguiendo cpp_apps.md §5. NO autoregistrarla sin confirmacion del usuario.

4. Copiar a /mnt/c/Users/lucas/Desktop/apps/<APP>/

Layout estandar (convencion local_files/, ver cpp_apps.md §7):

Desktop/apps/<APP>/
├── <APP>.exe                     ← binario
├── *.ttf, *.dll                  ← fuentes + DLLs (junto al exe)
├── assets/                       ← opcional, copiada del build
├── enrichers/                    ← opcional, si <app_dir>/enrichers existe
├── runtime/                      ← opcional, si app.md tiene python_runtime: true
└── local_files/                  ← creado por la app al primer arranque
                                    NUNCA borrar al recompilar
DEST="/mnt/c/Users/lucas/Desktop/apps/$APP_ARG"
mkdir -p "$DEST"

EXE_SRC="$BUILD_WIN/apps/$APP_ARG/$APP_ARG.exe"
if [ ! -f "$EXE_SRC" ]; then
  echo "ERROR: no se ha generado $EXE_SRC"
  exit 1
fi

# 1. Binario + TTFs + DLLs (junto al exe del build, copiados por add_imgui_app).
cp -v "$EXE_SRC" "$DEST/"
find "$BUILD_WIN/apps/$APP_ARG" -maxdepth 1 -type f \
    \( -name '*.ttf' -o -name '*.dll' \) -exec cp -v {} "$DEST/" \;

# 2. assets/ del build (opcional).
if [ -d "$BUILD_WIN/apps/$APP_ARG/assets" ]; then
  rsync -a --delete "$BUILD_WIN/apps/$APP_ARG/assets/" "$DEST/assets/"
fi

# 3. enrichers/ del app_dir (opcional). Excluye __pycache__ + .pyc.
if [ -d "$APP_DIR/enrichers" ]; then
  rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
      "$APP_DIR/enrichers/" "$DEST/enrichers/"
fi

# 4. runtime/ Python embebido (si la app lo declara).
#    Lee `python_runtime: true` del frontmatter de app.md.
if grep -q '^python_runtime:[[:space:]]*true' "$APP_DIR/app.md" 2>/dev/null; then
  if [ ! -d "$APP_DIR/runtime/python" ] || \
     [ "$APP_DIR/app.md" -nt "$APP_DIR/runtime/.lock" ]; then
    echo "[freeze] regenerando runtime Python (Windows) para $APP_ARG"
    "$APP_DIR/tools/freeze_python_runtime.sh" "$APP_DIR" windows
  fi
  rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
      "$APP_DIR/runtime/" "$DEST/runtime/"
fi

# 5. NO TOCAR local_files/. Si existe en $DEST, preservar — contiene
#    estado del usuario (DBs, settings, layouts ImGui, proyectos).
echo "OK: $APP_ARG -> $DEST"
[ -d "$DEST/local_files" ] && echo "    local_files/ preservado: $(du -sh "$DEST/local_files" | cut -f1)"

5. Compilar Android (solo si TARGETS contiene android)

Hoy no hay apps Android en el registry, asi que esta rama no se ejecuta. Cuando se anada la primera, este es el patron previsto:

if [[ " ${TARGETS[*]} " == *" android "* ]]; then
  ANDROID_DIR="$APP_DIR"
  [ -d "$APP_DIR/android" ] && ANDROID_DIR="$APP_DIR/android"

  cd "$ANDROID_DIR"
  if [ -x "./gradlew" ]; then
    ./gradlew assembleRelease
    APK="$(find "$ANDROID_DIR/app/build/outputs/apk/release" -name '*.apk' | head -n1)"
    [ -n "$APK" ] && cp -v "$APK" "/mnt/c/Users/lucas/Desktop/apps/$APP_ARG/"
  else
    echo "android: no hay ./gradlew en $ANDROID_DIR — saltando."
  fi
fi

Cuando llegue la primera app Android, este bloque puede ampliarse (firma, ABI splits, etc.).

6. Resumen

Imprime al final una linea por target con:

  • Tamano del binario (ls -lh)
  • Path final en /mnt/c/Users/lucas/Desktop/apps/<APP>/
  • Nombre del exe/apk

Notas

  • El build de Windows usa cpp/build/windows/ (no cpp/build/). El de Linux es cpp/build/. Coexisten sin conflicto.
  • El toolchain mingw-w64.cmake ya configura linkado estatico (-static-libgcc -static-libstdc++ -lwinpthread) — el .exe resultante es self-contained y no necesita DLLs en el escritorio.
  • Si se pasa una app que vive en projects/<proj>/apps/<APP>/, el target CMake sigue siendo <APP> (registrado en cpp/CMakeLists.txt con add_subdirectory(${PROJ_DIR}/apps/<APP> ${CMAKE_BINARY_DIR}/apps/<APP>)).
  • NO tocar AdminLocal ni instalar nada en Program Files — solo el escritorio del usuario.