EXLA's cached_make needs Mix.Tasks.Compile.ElixirMake (from elixir_make), which must be compiled first. mix deps.compile exla alone doesn't handle transitive compile-time deps. Use mix deps.compile without args to compile in dependency order.
69 lines
2.7 KiB
Text
69 lines
2.7 KiB
Text
# syntax=docker/dockerfile:1.6
|
|
#
|
|
# Pre-built CI test image. Extends the hexpm/elixir image with:
|
|
# * build-essential + git + curl + ca-certificates + cdo
|
|
# * hex + rebar
|
|
# * Precompiled EXLA NIF cached at ~/.cache/xla/
|
|
#
|
|
# When mix test runs with this image, EXLA's cached_make finds the
|
|
# precompiled libexla.so and skips the OOM-prone g++ C++ compilation
|
|
# entirely. Rebuild this image whenever mix.exs changes elixir/exla/xla
|
|
# versions or c_src/ files change (the cache key auto-invalidates).
|
|
#
|
|
# Built by .forgejo/workflows/build-ci-image.yaml.
|
|
|
|
ARG ELIXIR_IMAGE="docker.io/hexpm/elixir:1.20.1-erlang-29.0.2-debian-trixie-20260518-slim"
|
|
|
|
FROM ${ELIXIR_IMAGE} AS builder
|
|
|
|
# Install build tools and runtime test deps (cdo is needed by tests).
|
|
RUN export DEBIAN_FRONTEND=noninteractive && \
|
|
apt-get update -qq && \
|
|
apt-get install -y -qq git curl ca-certificates build-essential cdo && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install hex + rebar
|
|
RUN mix local.hex --force && mix local.rebar --force
|
|
|
|
# Precompile EXLA to populate ~/.cache/xla/ with both the downloaded
|
|
# XLA archive and the compiled libexla.so. We only need mix.exs +
|
|
# mix.lock + vendor + minimal config — no full project checkout.
|
|
WORKDIR /tmp/exla_warm
|
|
|
|
# Copy only what's needed for deps.get + deps.compile exla
|
|
COPY mix.exs mix.lock /tmp/exla_warm/
|
|
COPY vendor /tmp/exla_warm/vendor
|
|
|
|
# Minimal config — nx needs :default_backend to exist
|
|
RUN mkdir -p /tmp/exla_warm/config && \
|
|
printf 'import Config\nconfig :nx, :default_backend, EXLA.Backend\n' > /tmp/exla_warm/config/config.exs
|
|
|
|
# Fetch and compile all deps. EXLA's cached_make compiler calls
|
|
# Mix.Tasks.Compile.ElixirMake at build time, which requires elixir_make
|
|
# to be compiled first. mix deps.compile without arguments handles
|
|
# dependency ordering automatically. The cached_make step downloads the
|
|
# XLA archive, compiles the C++ NIF, and caches libexla.so at
|
|
# ~/.cache/xla/exla/{cache_key}/libexla.so.
|
|
RUN mix deps.get && mix deps.compile
|
|
|
|
# Clean up the temp project — the cache in ~/.cache/xla/ persists in
|
|
# the image and is what matters.
|
|
RUN rm -rf /tmp/exla_warm
|
|
|
|
FROM ${ELIXIR_IMAGE} AS final
|
|
|
|
# Install runtime test deps
|
|
RUN export DEBIAN_FRONTEND=noninteractive && \
|
|
apt-get update -qq && \
|
|
apt-get install -y -qq git curl ca-certificates build-essential cdo && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install hex + rebar
|
|
RUN mix local.hex --force && mix local.rebar --force
|
|
|
|
# Copy the precompiled EXLA cache from the builder stage. This is the
|
|
# ~/.cache/xla/ directory containing the XLA archive download and the
|
|
# compiled libexla.so keyed by exact Elixir/ERTS/exla/xla/c_src versions.
|
|
COPY --from=builder /root/.cache/xla /root/.cache/xla
|
|
|
|
WORKDIR /app
|