# ---- Stage 0: Compile lcgamboa QEMU as Linux .so (full ESP32 GPIO emulation) ---- FROM ubuntu:22.04 AS qemu-builder RUN apt-get update && apt-get install -y --no-install-recommends \ git python3 python3-pip python3-setuptools \ ninja-build pkg-config flex bison \ gcc g++ make ca-certificates \ libglib2.0-dev libgcrypt20-dev libslirp-dev \ libpixman-1-dev libfdt-dev \ && rm -rf /var/lib/apt/lists/* # QEMU 8.x requires meson >= 1.0 RUN pip3 install meson # Clone lcgamboa fork (picsimlab-esp32 branch adds GPIO/ADC/UART/RMT C callback API) RUN git clone --depth=1 --branch picsimlab-esp32 \ https://github.com/lcgamboa/qemu /qemu-lcgamboa WORKDIR /qemu-lcgamboa # Build libqemu-xtensa.so (ESP32/S3) and libqemu-riscv32.so (ESP32-C3). # The script: configures with --extra-cflags=-fPIC, builds normally, then # re-links without softmmu_main.c.o and with -shared. RUN bash build_libqemu-esp32.sh # ---- Stage 1: Build frontend and wokwi-libs ---- FROM node:20 AS frontend-builder WORKDIR /app # Clone wokwi-libs fresh from upstream to avoid stale submodule pointers. # git is pre-installed in the node:20 Debian image. RUN git clone --depth=1 https://github.com/wokwi/avr8js.git wokwi-libs/avr8js \ && git clone --depth=1 https://github.com/wokwi/rp2040js.git wokwi-libs/rp2040js \ && git clone --depth=1 https://github.com/wokwi/wokwi-elements.git wokwi-libs/wokwi-elements \ && git clone --depth=1 https://github.com/wokwi/wokwi-boards.git wokwi-libs/wokwi-boards # Build avr8js WORKDIR /app/wokwi-libs/avr8js RUN npm install && npm run build --if-present # Build rp2040js (may have no build script on some commits) WORKDIR /app/wokwi-libs/rp2040js RUN npm install && npm run build --if-present # Build wokwi-elements WORKDIR /app/wokwi-libs/wokwi-elements RUN npm install && npm run build --if-present # Build frontend # components-metadata.json is already committed; skip generate:metadata # (it requires wokwi-elements/src which isn't needed at runtime) WORKDIR /app COPY frontend/ frontend/ WORKDIR /app/frontend RUN npm install && npm run build:docker # ---- Stage 2: Final Production Image ---- FROM python:3.12-slim # Install system dependencies, nginx, and QEMU .so runtime libraries RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ nginx \ libglib2.0-0 \ libgcrypt20 \ libslirp0 \ libpixman-1-0 \ libfdt1 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install arduino-cli into /usr/local/bin directly (avoids touching /bin) RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \ | BINDIR=/usr/local/bin sh # Initialize arduino-cli config, add board manager URLs, then install cores # - RP2040: earlephilhower fork for Raspberry Pi Pico # - ESP32: Espressif official (covers ESP32, ESP32-S3, ESP32-C3, etc.) RUN arduino-cli config init \ && arduino-cli config add board_manager.additional_urls \ https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json \ && arduino-cli config add board_manager.additional_urls \ https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json \ && arduino-cli core update-index \ && arduino-cli core install arduino:avr \ && arduino-cli core install rp2040:rp2040 \ && arduino-cli core install esp32:esp32 WORKDIR /app # Data directory for persistent SQLite database (mounted as a volume at runtime) RUN mkdir -p /app/data # Install Python backend dependencies COPY backend/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy backend application code COPY backend/app/ ./app/ # Setup Nginx configuration COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf # Copy built frontend assets from builder stage COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html # Copy and configure entrypoint script COPY deploy/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh # ── ESP32 emulation: lcgamboa QEMU .so + ROM binaries ──────────────────────── # libqemu-xtensa.so → ESP32 / ESP32-S3 (Xtensa LX6/LX7) # libqemu-riscv32.so → ESP32-C3 (RISC-V RV32IMC) # esp32-v3-rom*.bin → boot/app ROM images required by esp32-picsimlab machine # esp32c3-rom.bin → ROM image required by esp32c3-picsimlab machine # NOTE: ROM files must live in the same directory as the .so (worker passes -L # to QEMU pointing at os.path.dirname(lib_path)) RUN mkdir -p /app/lib COPY --from=qemu-builder /qemu-lcgamboa/build/libqemu-xtensa.so /app/lib/ COPY --from=qemu-builder /qemu-lcgamboa/build/libqemu-riscv32.so /app/lib/ COPY --from=qemu-builder /qemu-lcgamboa/pc-bios/esp32-v3-rom.bin /app/lib/ COPY --from=qemu-builder /qemu-lcgamboa/pc-bios/esp32-v3-rom-app.bin /app/lib/ COPY --from=qemu-builder /qemu-lcgamboa/pc-bios/esp32c3-rom.bin /app/lib/ # Activate ESP32 emulation # QEMU_ESP32_LIB → Xtensa library (ESP32, ESP32-S3) # QEMU_RISCV32_LIB → RISC-V library (ESP32-C3 and variants) ENV QEMU_ESP32_LIB=/app/lib/libqemu-xtensa.so ENV QEMU_RISCV32_LIB=/app/lib/libqemu-riscv32.so EXPOSE 80 CMD ["/app/entrypoint.sh"]