# syntax=docker/dockerfile:1
FROM alpine:3.20 AS builder

# Fails with 17.0 succeeds with 16.4
ARG PG=17.0

USER root
RUN apk update && apk add --no-cache --update-cache \
  ca-certificates-bundle \
  util-linux-dev \
  clang17-dev \
  execline-dev \
  llvm18-dev \
  libedit-dev \
  libxml2-dev \
  build-base \
  net-tools \
  clang17 \
  zlib-dev \
  autoconf \
  automake \
  busybox \
  llvm18 \
  icu-dev \
  cmake \
  bison \
  flex \
  perl \
  curl \
  bash \
  flex
WORKDIR /app

RUN curl -L https://github.com/openssl/openssl/releases/download/openssl-3.3.2/openssl-3.3.2.tar.gz > openssl.tar.gz
RUN mkdir openssl && tar --extract --file=openssl.tar.gz --strip-components=1 --directory=openssl && rm openssl.tar.gz
RUN cd openssl && CC=clang CXX=clang++ perl ./Configure \
  linux-$(uname -m) \
  --prefix=/usr \
  --libdir=lib \
  --openssldir=/etc/ssl \
  enable-ktls \
  shared \
  no-zlib \
  no-async \
  no-comp \
  no-idea \
  no-mdc2 \
  no-rc5 \
  no-ec2m \
  no-sm2 \
  no-sm4 \
  no-ssl3 \
  no-seed \
  no-weak-ssl-ciphers \
  -Wa,--noexecstack && \
  perl configdata.pm --dump && \
  make -j$(nproc) && make install

RUN curl -L https://ftp.postgresql.org/pub/source/v$PG/postgresql-$PG.tar.bz2 > postgresql.tar.bz2
RUN mkdir postgresql && tar --extract --bzip2 --file=postgresql.tar.bz2 --strip-components=1 --directory=postgresql && rm postgresql.tar.bz2
RUN cd postgresql && CC=clang CXX=clang++ ./configure --with-openssl --with-libedit-preferred --with-uuid=e2fs --with-libxml --prefix=/usr/local
RUN cd postgresql/src/include && CC=clang CXX=clang++ make && make install
RUN cd postgresql/src/common && CC=clang CXX=clang++ make && make install
RUN cd postgresql/src/port && CC=clang CXX=clang++ make && make install
RUN cd postgresql/src/interfaces/libpq && CC=clang CXX=clang++ make && make install

COPY <<EOF ./main.cpp
#include <algorithm>
#include <iostream>
#include <chrono>
#include <thread>
#include <cstring>
#include <csignal>
#include <vector>

#include <libpq-fe.h>

void sig_handler(int /*_signo*/, siginfo_t * info, void * /*_ctx*/) {
  raise(info->si_signo);
  _exit(EXIT_FAILURE);
}

void registerSignalHandlers() {
  std::vector<int> signals = {
    // Signals for which the default action is "Core".
    SIGABRT, // Abort signal from abort(3)
    SIGBUS,  // Bus error (bad memory access)
    SIGFPE,  // Floating point exception
    SIGILL,  // Illegal Instruction
    SIGIOT,  // IOT trap. A synonym for SIGABRT
    SIGQUIT, // Quit from keyboard
    SIGSEGV, // Invalid memory reference
    SIGSYS,  // Bad argument to routine (SVr4)
    SIGTRAP, // Trace/breakpoint trap
    SIGXCPU, // CPU time limit exceeded (4.2BSD)
    SIGXFSZ, // File size limit exceeded (4.2BSD)
    SIGTERM
  };

  for (size_t i = 0; i < signals.size(); ++i) {
    struct sigaction action;
    memset(&action, 0, sizeof action);
    action.sa_flags = static_cast<int>(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND);
    sigfillset(&action.sa_mask);
    sigdelset(&action.sa_mask, signals[i]);
    action.sa_sigaction = &sig_handler;
    sigaction(signals[i], &action, nullptr);
  }
}

int main() {
  registerSignalHandlers();
  std::cout << "start" << std::endl;
  PGconn *conn = PQconnectdb("");
  if (conn == NULL) {
    std::cout << "fail" << std::endl;
  } else {
    if (PQstatus(conn) == CONNECTION_OK) {
      std::cout << "success" << std::endl;
    } else {
      fprintf(stderr, "fail: %s", PQerrorMessage(conn));
    }
    PQfinish(conn);
  }
  while (true) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  return 0;
}
EOF

# Fails with "-static" succeeds without
RUN CC=clang CXX=clang++ clang++ \
    -std=c++20 \
    -static \
    -flto \
    -O3 \
    -march=native \
    -Wpedantic \
    -Wall \
    -Wextra \
    -Wsign-conversion \
    -Wconversion \
    -o main main.cpp \
    -lpq \
    -lpgcommon \
    -lpgport \
    -lssl \
    -lcrypto \
    -lpthread \
    -ldl
ENTRYPOINT ["./main"]
CMD []
