# ---- 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 0.5: ESP-IDF toolchain for ESP32 compilation ---- FROM ubuntu:22.04 AS espidf-builder RUN apt-get update && apt-get install -y --no-install-recommends \ git wget flex bison gperf python3 python3-pip python3-venv \ cmake ninja-build ccache libffi-dev libssl-dev \ libusb-1.0-0 ca-certificates \ && rm -rf /var/lib/apt/lists/* # Install ESP-IDF 4.4.7 (matches Arduino ESP32 core 2.0.17 / lcgamboa QEMU ROM) RUN git clone -b v4.4.7 --recursive --depth=1 --shallow-submodules \ https://github.com/espressif/esp-idf.git /opt/esp-idf WORKDIR /opt/esp-idf # Install toolchains for esp32 (Xtensa) and esp32c3 (RISC-V) only RUN ./install.sh esp32,esp32c3 # Clean up large unnecessary files to reduce image size RUN rm -rf .git docs examples \ && find /root/.espressif -name '*.tar.*' -delete 2>/dev/null || true # Install Arduino-as-component for full Arduino API support in ESP-IDF builds RUN git clone --branch 2.0.17 --depth=1 --recursive --shallow-submodules \ https://github.com/espressif/arduino-esp32.git /opt/arduino-esp32 \ && rm -rf /opt/arduino-esp32/.git # ---- 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 \ cmake \ ninja-build \ libusb-1.0-0 \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir packaging # 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 # Only install arduino-cli binary here. Core installation (arduino:avr, # rp2040:rp2040) is done at first boot by entrypoint.sh and persisted # in the mounted /root/.arduino15 volume. # ESP32 compilation uses ESP-IDF instead of arduino-cli. 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 # ── ESP-IDF toolchain for ESP32 compilation ────────────────────────────────── # Copied from espidf-builder stage: IDF framework + cross-compiler toolchains COPY --from=espidf-builder /opt/esp-idf /opt/esp-idf COPY --from=espidf-builder /root/.espressif /root/.espressif COPY --from=espidf-builder /opt/arduino-esp32 /opt/arduino-esp32 ENV IDF_PATH=/opt/esp-idf ENV IDF_TOOLS_PATH=/root/.espressif ENV ARDUINO_ESP32_PATH=/opt/arduino-esp32 # Install ESP-IDF Python dependencies using the final image's Python # The requirements.txt has version constraints required by ESP-IDF 4.4.x RUN grep -v 'esp-windows-curses' /opt/esp-idf/requirements.txt \ | pip install --no-cache-dir -r /dev/stdin EXPOSE 80 CMD ["/app/entrypoint.sh"]