# Dockerfile — jerboa21/jerboa base image for static binary builds
#
# Provides:
#   - Chez Scheme (glibc) at /usr/local — built from the vendored vendor/ChezScheme
#   - Musl Chez Scheme (static) at /build/chez-musl — for linking, same source
#   - Jerboa library source at /build/mine/jerboa/lib
#   - jerboa-native-rs source + pre-built libjerboa_native.a (musl)
#   - Rust toolchain with x86_64-unknown-linux-musl target
#   - All common dependency repos cloned under /build/mine/
#   - musl-gcc, build-essential, and all linking deps pre-installed
#   - TUI deps: libvterm, libpcre2, pre-built Scintilla/Lexilla/Termbox archives
#   - jerboa-scintilla, jerboa-pcre2 repos
#
# Downstream projects use this as their FROM image to skip the expensive
# Chez double-build, Rust toolchain install, and repo cloning.
#
# Build & push:
#   make docker-build
#   make docker-push
#
# Or manually:
#   docker build --platform linux/amd64 -t jerboa21/jerboa .
#   docker push jerboa21/jerboa

ARG JERBOA_BASE_IMAGE
FROM ${JERBOA_BASE_IMAGE}

ARG JERBOA_BASE_IMAGE

# The base reference is deliberately supplied by the consumer-controlled lock
# rather than defaulting to a mutable tag.  Direct builds without an immutable
# reference fail while resolving FROM; a substituted tag is rejected here.
COPY support/container-inputs.sh support/container-dependencies.lock /tmp/jerboa-container/
ENV JERBOA_CONTAINER_LOCK=/tmp/jerboa-container/container-dependencies.lock
RUN /tmp/jerboa-container/container-inputs.sh check-base "$JERBOA_BASE_IMAGE" && \
    /tmp/jerboa-container/container-inputs.sh check
LABEL org.opencontainers.image.base.name="$JERBOA_BASE_IMAGE"

ARG DEBIAN_FRONTEND=noninteractive

# ── System dependencies for static builds ────────────────────────────────────
RUN snapshot=$(/tmp/jerboa-container/container-inputs.sh field ubuntu-noble 3) && \
    rm -f /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources && \
    { \
      echo "deb [check-valid-until=no] $snapshot noble main restricted universe multiverse"; \
      echo "deb [check-valid-until=no] $snapshot noble-updates main restricted universe multiverse"; \
      echo "deb [check-valid-until=no] $snapshot noble-security main restricted universe multiverse"; \
    } > /etc/apt/sources.list.d/jerboa-snapshot.list && \
    apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    musl-tools \
    musl-dev \
    git \
    openssh-client \
    ca-certificates \
    curl \
    libncurses-dev \
    uuid-dev \
    liblz4-dev \
    zlib1g-dev \
    libsqlite3-dev \
    pkg-config \
    file \
    && rm -rf /var/lib/apt/lists/*

# musl-tools only provides musl-gcc; Rust cc-rs needs musl-g++ for C++ deps.
# Use system g++ for C++ compilation (it has <sstream> etc. that musl-gcc lacks).
RUN printf '#!/bin/sh\nexec /usr/bin/g++ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 "$@"\n' \
      > /usr/local/bin/x86_64-linux-musl-g++ && \
    chmod +x /usr/local/bin/x86_64-linux-musl-g++ && \
    ln -sf /usr/local/bin/x86_64-linux-musl-g++ /usr/local/bin/musl-g++

# ── Rust toolchain ────────────────────────────────────────────────────────────
RUN /tmp/jerboa-container/container-inputs.sh fetch rustup-installer /tmp/rustup-init && \
    rust_version=$(/tmp/jerboa-container/container-inputs.sh field rust 4) && \
    audit_version=$(/tmp/jerboa-container/container-inputs.sh field cargo-audit 4) && \
    chmod 0755 /tmp/rustup-init && \
    /tmp/rustup-init -y --default-toolchain "$rust_version" --profile minimal && \
    rm -f /tmp/rustup-init && \
    . /root/.cargo/env && \
    rustup target add --toolchain "$rust_version" x86_64-unknown-linux-musl && \
    cargo install cargo-audit --version "=$audit_version" --locked

ENV PATH="/root/.cargo/bin:${PATH}"
ENV RUSTUP_HOME="/root/.rustup"

# Set HOME=/build so no real usernames or home directories leak into binaries
ENV HOME=/build
WORKDIR /build

# ── Vendored Chez Scheme source (merged into the jerboa repo) ────────────────
# Copied from vendor/ChezScheme in the build context (the jerboa repo root), so
# the image always tracks the in-repo Chez instead of an external fork.
COPY vendor/ChezScheme /build/chez-src

# ── Build Chez Scheme (glibc, for compilation steps) ─────────────────────────
# Installed to /usr/local so `scheme` is on PATH
RUN cp -a /build/chez-src /build/ChezScheme && cd /build/ChezScheme && \
    ./configure --threads --enable-harden --disable-x11 --installprefix=/usr/local && \
    make -j$(nproc) && \
    make install && \
    cd /build && rm -rf /build/ChezScheme

# ── Build Chez Scheme (musl, for static linking) ────────────────────────────
# Two-pass build:
#   Pass 1: Full build with stock gcc to generate boot files
#   Pass 2: Rebuild kernel only with musl-gcc --static, reusing boot files
# Installed to /build/chez-musl
RUN cp -a /build/chez-src /build/chez-musl-src && cd /build/chez-musl-src && \
    ./configure --threads --enable-harden --disable-x11 --installprefix=/build/chez-musl && \
    make -j$(nproc) && \
    cp ta6le/boot/ta6le/petite.boot /tmp/petite.boot && \
    cp ta6le/boot/ta6le/scheme.boot /tmp/scheme.boot && \
    make clean && \
    ./configure --threads --enable-harden --disable-x11 --static CC=musl-gcc --installprefix=/build/chez-musl && \
    mkdir -p ta6le/boot/ta6le && \
    cp /tmp/petite.boot ta6le/boot/ta6le/ && \
    cp /tmp/scheme.boot ta6le/boot/ta6le/ && \
    make -j$(nproc) kernel && \
    make install && \
    cd /build && rm -rf chez-musl-src /tmp/petite.boot /tmp/scheme.boot

# ── Copy Jerboa library + native Rust source ──────────────────────────────────
WORKDIR /build/mine
COPY jerbuild.ss /build/mine/jerboa/jerbuild.ss
COPY lib /build/mine/jerboa/lib
COPY support /build/mine/jerboa/support
COPY jerboa-native-rs /build/mine/jerboa/jerboa-native-rs

# ── Pre-build libjerboa_native.a (musl, no duckdb) ───────────────────────────
# Warms the Cargo registry cache under /build/.cargo so downstream builds that
# patch regex_native.rs only need to recompile that one module, not fetch crates.
# Uses CARGO_HOME=/build/.cargo so no /root/.cargo paths leak into the .a.
RUN cd /build/mine/jerboa/jerboa-native-rs && \
    grep -q '#\[cfg(feature = "duckdb")\]' src/lib.rs || \
    sed -i 's/^mod duckdb_native;/#[cfg(feature = "duckdb")]\nmod duckdb_native;/' src/lib.rs && \
    CARGO_HOME=/build/.cargo \
    RUSTFLAGS="--remap-path-prefix /build/.cargo/registry/src=crate --remap-path-prefix /build/mine=src" \
    cargo build --locked --release --target x86_64-unknown-linux-musl --no-default-features && \
    strip -S target/x86_64-unknown-linux-musl/release/libjerboa_native.a

# ── Materialize pinned common dependency repos ───────────────────────────────
RUN for name in \
      jerboa-ssh jerboa-sqlite jerboa-crypto jerboa-ssl jerboa-https \
      jerboa-awk jerboa-sed jerboa-aws jerboa-fuse jerboa-scintilla \
      jerboa-pcre2; do \
      /tmp/jerboa-container/container-inputs.sh fetch-git "$name"; \
    done

# ── TUI dependencies: libvterm, libpcre2, ncurses (static) ─────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
    libvterm-dev \
    libpcre2-dev \
    && rm -rf /var/lib/apt/lists/*

# ── Build Scintilla + Lexilla + Termbox static archives from source ────────
# These are needed by jerboa-emacs TUI for the embedded terminal editor.
# Layout: sci-vendor/scintilla/ (stock Scintilla + termbox backend inside)
#         sci-vendor/lexilla/   (Lexilla from scintilla.org)
# 1. Download stock Scintilla + Lexilla tarballs
# 2. Clone scintilla-termbox backend into scintilla/termbox/
# 3. Clone termbox_next into scintilla/termbox/termbox_next/
# 4. Build all three static archives
RUN mkdir -p /build/sci-vendor && \
    /tmp/jerboa-container/container-inputs.sh extract-tgz scintilla && \
    /tmp/jerboa-container/container-inputs.sh extract-tgz lexilla && \
    /tmp/jerboa-container/container-inputs.sh fetch-git scintilla-termbox && \
    /tmp/jerboa-container/container-inputs.sh fetch-git termbox-next && \
    cd /build/sci-vendor/scintilla/termbox/termbox_next && make -j$(nproc) && \
    cd /build/sci-vendor/scintilla/termbox && make -j$(nproc) && \
    cd /build/sci-vendor/lexilla/src && make -j$(nproc) && \
    echo "Scintilla/Lexilla/Termbox static archives built"

# ── Set default environment for downstream builds ───────────────────────────
ENV JERBOA_MUSL_CHEZ_PREFIX=/build/chez-musl
ENV JERBOA_HOME=/build/mine/jerboa
ENV JERBOA=/build/mine/jerboa/lib
ENV AWK_DIR=/build/mine/jerboa-awk/lib
ENV SED_DIR=/build/mine/jerboa-sed/lib
ENV AWS_DIR=/build/mine/jerboa-aws/lib
ENV JERBOA_FUSE_DIR=/build/mine/jerboa-fuse/lib
ENV JERBOA_SCINTILLA_DIR=/build/mine/jerboa-scintilla/src
ENV JERBOA_PCRE2_DIR=/build/mine/jerboa-pcre2
ENV SCI_VENDOR_DIR=/build/sci-vendor

# Retain the resolved non-Cargo graph in the image itself.  The checked lock
# captures remote identity; dpkg/toolchain records capture what the snapshot
# resolver and compilers actually selected.
RUN mkdir -p /usr/local/share/jerboa/build-graph && \
    cp /tmp/jerboa-container/container-dependencies.lock \
      /usr/local/share/jerboa/build-graph/container-dependencies.lock && \
    dpkg-query -W -f='${binary:Package}\t${Version}\n' \
      | LC_ALL=C sort > /usr/local/share/jerboa/build-graph/debian-packages.tsv && \
    { \
      rustc --version; \
      cargo --version; \
      scheme --version; \
      musl-gcc --version | head -1; \
    } > /usr/local/share/jerboa/build-graph/toolchains.txt 2>&1

# ── Smoke test ───────────────────────────────────────────────────────────────
RUN scheme --version && \
    musl-gcc --version | head -1 && \
    cargo --version && \
    test -d /build/chez-musl && \
    test -f /build/mine/jerboa/jerboa-native-rs/target/x86_64-unknown-linux-musl/release/libjerboa_native.a && \
    test -f /build/sci-vendor/scintilla/bin/scintilla.a && \
    test -f /build/sci-vendor/lexilla/bin/liblexilla.a && \
    test -f /build/sci-vendor/scintilla/termbox/termbox_next/bin/termbox.a && \
    echo "jerboa21/jerboa base image ready (with TUI deps)"

WORKDIR /build
CMD ["/bin/bash"]
