# syntax=docker/dockerfile:1
#
# The container of MDIR: HDF5, LLVM/MLIR 23.1.2, and MDIR built with CUDA,
# and a small runtime image with the installed tree. Build from the root of
# the repository:
#
#   docker build -f packaging/Dockerfile -t mdir:0.1.0 \
#     --build-arg MDIR_GIT_COMMIT=$(git rev-parse HEAD) .
#
# and run on a GPU with the NVIDIA container toolkit:
#
#   docker run --rm --gpus all -v "$PWD":/work mdir:0.1.0 run md.toml
#
# Layers go from what changes least to what changes most, so that a rebuild
# reuses the cached ones:
#   1. the base image (pinned by digest) and the system packages;
#   2. HDF5, from scripts/build-hdf5.sh alone;
#   3. LLVM, MLIR, and the OpenMP runtime, from scripts/build-llvm.sh alone,
#      the slowest layer (about an hour with 32 jobs);
#   4. the sources of MDIR, last.
# Each stage copies only the files it needs: a change to MDIR's sources
# leaves the HDF5 and LLVM layers cached, and a rebuild then compiles MDIR
# alone, incrementally, in a build tree kept in a BuildKit cache mount.
# MDIR has no package manifest of its own: its dependencies are exactly the
# two scripts above and the CUDA of the base image.
#
# Build arguments: JOBS and LINK_JOBS bound the parallel compile and link
# jobs of LLVM; MDIR_GIT_COMMIT names the commit, since the context holds no
# .git. To change CUDA, change both digests below together (the CUDA of the
# image must not be newer than the host driver supports).

# nvidia/cuda:13.0.1-devel-ubuntu24.04 and -runtime-ubuntu24.04.
ARG DEVEL=nvidia/cuda:13.0.1-devel-ubuntu24.04@sha256:7d2f6a8c2071d911524f95061a0db363e24d27aa51ec831fcccf9e76eb72bc92
ARG RUNTIME=nvidia/cuda:13.0.1-runtime-ubuntu24.04@sha256:c3fde347d52d578c84fd644bc177bc7ec333feaf11550d990da4084d7612e4c7

# --- 1. System packages ------------------------------------------------------
FROM ${DEVEL} AS base
RUN apt-get update \
 && apt-get install -y --no-install-recommends \
      build-essential cmake ninja-build git python3 ca-certificates curl \
      zlib1g-dev \
 && rm -rf /var/lib/apt/lists/*

# --- 2. HDF5 1.14.6 ----------------------------------------------------------
FROM base AS hdf5
ARG JOBS=16
COPY scripts/build-hdf5.sh /src/scripts/build-hdf5.sh
RUN HDF5_ROOT=/opt/hdf5 HDF5_VERSION=1.14.6 JOBS=${JOBS} \
      /src/scripts/build-hdf5.sh \
 && rm -rf /opt/hdf5/src /opt/hdf5/build

# --- 3. LLVM, MLIR, and the OpenMP runtime 23.1.2 ----------------------------
FROM hdf5 AS toolchain
ARG JOBS=16
ARG LINK_JOBS=4
COPY scripts/build-llvm.sh /src/scripts/build-llvm.sh
# Only the targets MDIR compiles for: the host and NVIDIA GPUs.
RUN LLVM_ROOT=/opt/llvm LLVM_VERSION=23.1.2 LLVM_TARGETS="X86;NVPTX" \
      JOBS=${JOBS} LINK_JOBS=${LINK_JOBS} /src/scripts/build-llvm.sh \
 && rm -rf /opt/llvm/src /opt/llvm/build

# --- 4. MDIR -----------------------------------------------------------------
FROM toolchain AS build
ARG MDIR_GIT_COMMIT=
ENV MDIR_GIT_COMMIT=${MDIR_GIT_COMMIT}
COPY . /src/mdir
# The build tree stays in a cache mount between builds, so that a change of
# a few sources recompiles only those; the installed tree is in the layer.
RUN --mount=type=cache,target=/build/mdir,id=mdir-build \
    cmake -G Ninja -S /src/mdir -B /build/mdir \
      -DCMAKE_BUILD_TYPE=Release \
      -DLLVM_DIR=/opt/llvm/23.1.2/lib/cmake/llvm \
      -DMLIR_DIR=/opt/llvm/23.1.2/lib/cmake/mlir \
      -DHDF5_ROOT=/opt/hdf5/1.14.6 \
      -DCUDAToolkit_ROOT=/usr/local/cuda \
      -DMDIR_ENABLE_CUDA=ON \
      -DCMAKE_INSTALL_PREFIX=/opt/mdir \
 && cmake --build /build/mdir --target mdir mdrt mdrt_cuda \
 && rm -rf /opt/mdir \
 && cmake --install /build/mdir

# --- CI: HDF5, LLVM, MLIR, and the tools of the tests, without CUDA --------
# The image of the CPU tier of GitHub Actions (#611): `docker build --target
# ci`, which .github/workflows/ci-image.yml builds and pushes to
# ghcr.io/vv137/mdir-ci, tagged with a hash of this file and of the two
# build scripts, and .github/workflows/ci.yml pulls. It shares the two build
# scripts with the stages above, without CUDA (the GPU tests are
# unsupported there) and without MDIR, which each CI run builds. Ubuntu
# 24.04, pinned by digest. It stands before the runtime stage, which stays
# the last and so the default target.
FROM ubuntu:24.04@sha256:68ee22004a9109b44973313b02ff7b1de250f1de0f5ecb628d73f579aaff33ac AS ci-base
RUN apt-get update \
 && apt-get install -y --no-install-recommends \
      build-essential cmake ninja-build git python3 python3-dev python3-venv \
      ca-certificates curl zlib1g-dev ccache lld \
 && rm -rf /var/lib/apt/lists/*
# The interpreter of the tests: lit, pybind11 (the version CMake requires),
# and NumPy, in a virtual environment on the path.
RUN python3 -m venv /opt/venv \
 && /opt/venv/bin/pip install --no-cache-dir lit==23.1.2 pybind11==3.0.1 'numpy>=1.23'
ENV PATH=/opt/venv/bin:${PATH}

FROM ci-base AS ci-hdf5
ARG JOBS=4
COPY scripts/build-hdf5.sh /src/scripts/build-hdf5.sh
RUN HDF5_ROOT=/opt/hdf5 HDF5_VERSION=1.14.6 JOBS=${JOBS} \
      /src/scripts/build-hdf5.sh \
 && rm -rf /opt/hdf5/src /opt/hdf5/build

FROM ci-hdf5 AS ci
ARG JOBS=4
ARG LINK_JOBS=2
COPY scripts/build-llvm.sh /src/scripts/build-llvm.sh
RUN LLVM_ROOT=/opt/llvm LLVM_VERSION=23.1.2 LLVM_TARGETS="X86;NVPTX" \
      JOBS=${JOBS} LINK_JOBS=${LINK_JOBS} /src/scripts/build-llvm.sh \
 && rm -rf /opt/llvm/src /opt/llvm/build

# The same image from an install of LLVM that the jobs of ci-image.yml built
# with scripts/build-llvm.sh, in parts that share a compile cache, since one
# hosted job cannot build it within its six hours: the context holds the
# install tree as ci-llvm/.
FROM ci-hdf5 AS ci-prebuilt
COPY ci-llvm/ /opt/llvm/23.1.2/

# --- Runtime -----------------------------------------------------------------
# The runtime image of CUDA carries cuFFT; the kernels also take the math
# functions of libdevice, from CUDA_ROOT, which only the devel image has.
# The libraries and libdevice come before the installed tree, which changes
# with every build.
#
# The image distributes in binary form what it copies here, so each copy
# comes with the notice its license asks for (packaging/licenses/README.md;
# scripts/release/check-image.sh checks them): HDF5's COPYING and MDIR's
# license in /opt/mdir/share/mdir, beside the notices of pocketfft, toml++,
# LLVM, and the OpenMP runtime that the install of MDIR puts in
# /opt/mdir/share/mdir/licenses; and the CUDA EULA, whose Attachment A lists
# libdevice as distributable, as /usr/local/cuda/EULA.txt. The Debian
# package of libdevice (libnvvm-13-0, of the devel image) carries the EULA
# as its copyright file: change its name with the CUDA of DEVEL.
FROM ${RUNTIME}
COPY --from=hdf5 /opt/hdf5/1.14.6/lib /opt/hdf5/1.14.6/lib
COPY --from=hdf5 /opt/hdf5/1.14.6/share/COPYING /opt/mdir/share/mdir/licenses/HDF5-COPYING
COPY --from=base /usr/local/cuda/nvvm/libdevice /usr/local/cuda/nvvm/libdevice
COPY --from=base /usr/share/doc/libnvvm-13-0/copyright /usr/local/cuda/EULA.txt
ENV PATH=/opt/mdir/bin:${PATH} \
    CUDA_ROOT=/usr/local/cuda
COPY examples /opt/mdir/share/mdir/examples
COPY LICENSE /opt/mdir/share/mdir/LICENSE
COPY --from=build /opt/mdir /opt/mdir
WORKDIR /work
ENTRYPOINT ["mdir"]
CMD ["doctor"]
