#################################################################################################
# The "build-deps" stage
# - Installs all compilers/interpreters, tools, and OS packages on the given debian or ubuntu image
#################################################################################################
# Supports europe-west3-docker.pkg.dev/o1labs-192920/euro-docker-repo/debian:bullseye-slim, and europe-west3-docker.pkg.dev/o1labs-192920/euro-docker-repo/ubuntu:focal
ARG image=europe-west3-docker.pkg.dev/o1labs-192920/euro-docker-repo/ubuntu:focal
ARG deb_codename
ARG apt_cache_url=""

FROM ${image} AS build-deps
ARG TARGETARCH

ARG apt_cache_url

# OCaml Version
# The version must be the same as the version used in:
# - dockerfiles/toolchain/1-build-deps
# - opam.export
# - scripts/update_opam_switch.sh
ARG OCAML_VERSION=4.14
ARG OCAML_REVISION=.2
ARG OCAML_VARIANT=
ARG OCAML_PACKAGE=
ARG OPAM_VERSION=2.3.0
# The version must be the same as the version used in:
# - dockerfiles/toolchain/1-build-deps
# - flake.nix (and flake.lock after running
#   `nix flake update opam-repository`).
# - scripts/update_opam_switch.sh
ARG OPAM_REPOSITORY_COMMIT=08d8c16c16dc6b23a5278b06dff0ac6c7a217356
ARG O1LABS_OPAM_REPOSITORY_COMMIT=dd90c5c72b7b7caeca3db3224b2503924deea08a

# Golang version number used to detemine tarball name
ARG GO_VERSION=1.19.11

# Go Capnp Version (for capnpc dependency)
ARG GO_CAPNP_VERSION=v3.0.0-alpha.5

# Rust Version passed into rustup-init, can also be "stable", "nightly" or similar
# This should stay in line with:
# - src/lib/crypto/kimchi_bindings/stubs/rust-toolchain.toml
# - src/lib/crypto/proof-systems/rust-toolchain.toml
ARG RUST_VERSION=1.92.0
# Nightly Rust Version used for WebAssembly builds
# This should stay in line with the value of the variable
# $NIGHTLY_RUST_VERSION defined in:
# - src/lib/crypto/proof-systems/Makefile
ARG RUST_NIGHTLY=2024-09-05

# --- OS package dependencies
# Organized as two alphabetized lists, first libraries and then tools/other packages
ENV DEBIAN_FRONTEND=noninteractive
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Optional: configure APT caching proxy
COPY scripts/configure-apt-proxy.sh /usr/local/bin/configure-apt-proxy.sh
RUN chmod +x /usr/local/bin/configure-apt-proxy.sh \
  && /usr/local/bin/configure-apt-proxy.sh "$apt_cache_url"

RUN CODENAME=$(grep "VERSION_CODENAME" /etc/os-release | cut -d= -f2); \
  case "${CODENAME}" in \
    bookworm) LIBSSL="libssl-dev"; LIBPROCPS="libproc2-0" ;; \
    bullseye) LIBSSL="libssl-dev"; LIBPROCPS="libprocps-dev" ;; \
    focal) LIBSSL="libssl-dev"; LIBPROCPS="libprocps-dev" ;; \
    jammy) LIBSSL="libssl3"; LIBPROCPS="libprocps8";; \
    noble) LIBSSL="libssl3t64"; LIBPROCPS="libproc2-0";; \
    *) echo "Unsupported codename: ${CODENAME}"; exit 1 ;; \
  esac; \
  apt-get update --quiet \
  && apt-get upgrade --quiet --yes \
  && apt-get install --no-install-recommends --quiet --yes \
  adduser \
  libboost-dev \
  libboost-program-options-dev \
  libbz2-dev \
  libcap-dev \
  libffi-dev \
  libgflags-dev \
  libgmp-dev \
  libgmp3-dev \
  libjemalloc-dev \
  liblmdb-dev \
  liblmdb0 \
  libpq-dev \
  libsodium-dev \
  "$LIBSSL" \
  "$LIBPROCPS" \
  build-essential \
  ca-certificates \
  capnproto \
  cmake \
  curl \
  file \
  git \
  git-lfs \
  lld \
  m4 \
  pkg-config \
  rocksdb-tools \
  rsync \
  sudo \
  unzip \
  zlib1g-dev \
  && rm -rf /var/lib/apt/lists/*

# --- Create opam user (for later) and give sudo to make opam happy
RUN adduser --uid 65533 --disabled-password --gecos '' opam \
  && passwd -l opam \
  && chown -R opam:opam /home/opam \
  && echo 'opam ALL=(ALL:ALL) NOPASSWD:ALL' > /etc/sudoers.d/opam \
  && chmod 440 /etc/sudoers.d/opam \
  && chown root:root /etc/sudoers.d/opam \
  && chmod 777 /tmp

# --- Quiet git warnings about detatched head states, which are used frequently in later stages
RUN git config --global advice.detachedHead false

# --- Opam install of a given OPAM_VERSION from github release
RUN case "${TARGETARCH}" in \
        "amd64") OPAM_ARCH="x86_64" ;; \
        "arm64") OPAM_ARCH="arm64" ;; \
        *) echo "Unsupported arch: ${TARGETARCH}"; exit 1 ;; \
    esac; \
    curl -sL \
  "https://github.com/ocaml/opam/releases/download/${OPAM_VERSION}/opam-${OPAM_VERSION}-${OPAM_ARCH}-linux" \
  -o /usr/bin/opam \
  && chmod +x /usr/bin/opam

# --- Golang install of a given GO_VERSION (add -v for spam output of each file from the go dist)
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -s "https://dl.google.com/go/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" | tar -xz -C /usr/lib/

# --- Rust install via rustup-init to a given RUST_VERSION
# --- Additionally, install RUST_NIGHTLY via rustup
# For more about rustup-init see: https://github.com/rust-lang/rustup/blob/master/README.md
# As opposed to introducing another shell script here (that mostly just determines the platform)
# we just download the binary for the only platform we care about in this docker environment
USER opam
RUN case "${TARGETARCH}" in \
        "amd64") RUSTUP_ARCH="x86_64" ;; \
        "arm64") RUSTUP_ARCH="aarch64" ;; \
        *) echo "Unsupported arch: ${TARGETARCH}"; exit 1 ;; \
    esac; \
  curl --proto "=https" \
  --tlsv1.2 -sSf -o /tmp/rustup-init \
  https://static.rust-lang.org/rustup/dist/${RUSTUP_ARCH}-unknown-linux-gnu/rustup-init \
  && chmod +x /tmp/rustup-init \
  && /tmp/rustup-init -y --default-toolchain \
    "${RUST_VERSION}" \
    --profile minimal \
    --component rust-src \
    --target wasm32-unknown-unknown \
  && "$HOME/.cargo/bin/rustup" toolchain install "nightly-${RUST_NIGHTLY}" \
    --profile minimal \
    --component rust-src \
    --target wasm32-unknown-unknown \
    --no-self-update \
  && rm /tmp/rustup-init
USER root

###########################################################################################
# Initialize opam in a minimal fashion
###########################################################################################

# Set up environment for running as opam user
WORKDIR /home/opam
USER opam
ENV HOME=/home/opam

# --- Create the following user directory configs as the Opam user:
## Add go + rust to the path, unlimit the opam user,
## unlimit stack for future shells that might use spacetime,
## disable ipv6
## disable sandboxing to allow unprivledged builds
RUN mkdir --mode=700 /home/opam/.gnupg \
  && echo "export PATH=\"\$PATH:/usr/lib/go/bin:\$HOME/.cargo/bin\"" >> /home/opam/.bashrc \
  && echo "ulimit -s unlimited" >> /home/opam/.bashrc \
  && echo "disable-ipv6" >> /home/opam/.gnupg/dirmngr.conf

ENV PATH="$PATH:/usr/lib/go/bin:$HOME/.cargo/bin"

# --- OCaml install of a given OCAML_VERSION via opam switch
# additionally initializes opam with sandboxing disabled, as we did not install
# bubblewrap above.
RUN git clone \
  https://github.com/ocaml/opam-repository.git \
  --depth 1 \
  /home/opam/opam-repository
# Pin OPAM repo to specific commit, o.w. after this commit some of our direct
# dependencies are removed
WORKDIR /home/opam/opam-repository
RUN git fetch origin "${OPAM_REPOSITORY_COMMIT}" \
  && git checkout "${OPAM_REPOSITORY_COMMIT}"

RUN opam init --disable-sandboxing \
    -k git \
    -a /home/opam/opam-repository \
    --bare
# We pin the opam-repo SHA so this step gets 
# cache-busted when we update the opam repo
RUN opam repository add --yes \
    --all \
    --set-default o1-labs \
    https://github.com/o1-labs/opam-repository.git#${O1LABS_OPAM_REPOSITORY_COMMIT} \
  && opam switch create "${OCAML_VERSION}${OCAML_REVISION}" \
    "${OCAML_PACKAGE}${OCAML_VERSION}${OCAML_REVISION}${OCAML_VARIANT}" \
  && opam switch "${OCAML_VERSION}${OCAML_REVISION}"

WORKDIR /home/opam

# --- Build and install capnproto go compiler
# This package was particularly tricky to get to install correctly because of
# how the package's source code is structured.
# To work around this, we install it as a dependency for a local go module and
# build the binary from the GOPATH.
RUN mkdir go-tmp
WORKDIR /home/opam/go-tmp
RUN /usr/lib/go/bin/go mod init local/build \
  && /usr/lib/go/bin/go get capnproto.org/go/capnp/v3@$GO_CAPNP_VERSION \
  && /usr/lib/go/bin/go build -o capnpc-go capnproto.org/go/capnp/v3/capnpc-go

USER root
RUN mv /home/opam/go-tmp/capnpc-go /usr/local/bin
USER opam

WORKDIR /home/opam
RUN rm -rf go-tmp
