# ════════════════════════════════════════════════════════════════════════════
# GNU-free CMake build for portable OpenSSH (FenrirOS)
#
# Replaces OpenSSH's autotools GNU-make build with CMake + Ninja so the whole
# pipeline stays GNU-free (driven by makeplus/ninja and the LLVM/musl toolchain).
#
# OpenSSH still needs its POSIX-sh ./configure run ONCE to probe the platform and
# emit config.h (+ the openbsd-compat headers and the *_config man/config files).
# configure is an autoconf-generated /bin/sh script — not GNU make — so running
# it does not reintroduce a GNU build tool. build_openssh() in build-os.sh runs
# ./configure before invoking cmake; this file then compiles and links every
# target. The object lists below mirror OpenSSH 9.9p2 Makefile.in verbatim
# (sources self-disable via config.h #ifdefs for --without-openssl/--without-pam).
# ════════════════════════════════════════════════════════════════════════════
cmake_minimum_required(VERSION 3.13)
project(openssh C)

if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.h")
    message(FATAL_ERROR
        "config.h not found in the OpenSSH source root.\n"
        "Run ./configure (build_openssh does this) before invoking cmake.")
endif()

# ── Install layout (mirrors OpenSSH bindir/sbindir/libexecdir) ───────────────
set(OPENSSH_SYSCONFDIR  "/etc/ssh"   CACHE STRING "ssh system config dir")
set(OPENSSH_PRIVSEP_DIR "/var/empty" CACHE STRING "privilege-separation chroot")
set(OPENSSH_PIDDIR      "/var/run"   CACHE STRING "pid file dir")

set(_bindir     "${CMAKE_INSTALL_PREFIX}/bin")
set(_libexecdir "${CMAKE_INSTALL_PREFIX}/libexec")

# musl sysroot (zlib lives here; its header was staged in by build_openssh).
set(MUSL_SYSROOT "/usr/local/musl" CACHE PATH "musl sysroot prefix")

# NB: openbsd-compat is intentionally NOT on the global include path. Upstream
# only uses `-I. -I$(srcdir)`; the main sources reach the compat headers via the
# explicit "openbsd-compat/..." prefix. Putting openbsd-compat on the search
# path makes angle-bracket includes like <fnmatch.h> resolve to the empty compat
# stub instead of musl's real header (breaks scp.c's fnmatch, etc.).
include_directories(
    "${CMAKE_CURRENT_SOURCE_DIR}"
    "${MUSL_SYSROOT}/include"
)
link_directories("${MUSL_SYSROOT}/lib")

# config.h carries the platform feature defines; the path macros below come from
# the Makefile's CPPFLAGS PATHS block (resolved against the install prefix).
add_compile_definitions(
    HAVE_CONFIG_H
    _XOPEN_SOURCE=600
    _BSD_SOURCE
    _DEFAULT_SOURCE
    _GNU_SOURCE
    SSHDIR="${OPENSSH_SYSCONFDIR}"
    _PATH_SSH_PROGRAM="${_bindir}/ssh"
    _PATH_SSH_ASKPASS_DEFAULT="${_libexecdir}/ssh-askpass"
    _PATH_SFTP_SERVER="${_libexecdir}/sftp-server"
    _PATH_SSH_KEY_SIGN="${_libexecdir}/ssh-keysign"
    _PATH_SSHD_SESSION="${_libexecdir}/sshd-session"
    _PATH_SSH_PKCS11_HELPER="${_libexecdir}/ssh-pkcs11-helper"
    _PATH_SSH_SK_HELPER="${_libexecdir}/ssh-sk-helper"
    _PATH_SSH_PIDDIR="${OPENSSH_PIDDIR}"
    _PATH_PRIVSEP_CHROOT_DIR="${OPENSSH_PRIVSEP_DIR}"
)

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release")
endif()
add_compile_options(
    -O2 -fPIC -fno-strict-aliasing
    -Wno-pointer-sign -Wno-unused-parameter -Wno-unused-result
)

# ── openbsd-compat → libopenbsd-compat.a (OPENBSD + COMPAT + PORTS) ──────────
set(_compat_names
    arc4random arc4random_uniform base64 basename bcrypt_pbkdf bindresvport
    blowfish daemon dirname explicit_bzero fmt_scaled freezero fnmatch getcwd
    getgrouplist getopt_long getrrsetbyname glob inet_aton inet_ntoa inet_ntop
    md5 memmem mktemp pwcache readpassphrase reallocarray recallocarray rresvport
    setenv setproctitle sha1 sha2 sigact strcasestr strlcat strlcpy strmode
    strndup strnlen strptime strsep strtoll strtonum strtoull strtoul
    timingsafe_bcmp vis
    bsd-asprintf bsd-closefrom bsd-cygwin_util bsd-err bsd-flock bsd-getentropy
    bsd-getline bsd-getpagesize bsd-getpeereid bsd-malloc bsd-misc bsd-nextstep
    bsd-openpty bsd-poll bsd-pselect bsd-setres_id bsd-signal bsd-snprintf
    bsd-statvfs bsd-timegm bsd-waitpid fake-rfc2553 getrrsetbyname-ldns
    kludge-fd_set openssl-compat libressl-api-compat xcrypt
    port-aix port-irix port-linux port-prngd port-solaris port-net port-uw
)
set(_compat_src "")
foreach(_n ${_compat_names})
    list(APPEND _compat_src "openbsd-compat/${_n}.c")
endforeach()
add_library(openbsd-compat STATIC ${_compat_src})
# Upstream compiles the compat objects from inside openbsd-compat/ (cd && make),
# so their own `<vis.h>`/`<readpassphrase.h>` includes resolve to the local
# stubs. Replicate that with a target-private include dir (kept off the global
# path so the main sources still get musl's real headers).
target_include_directories(openbsd-compat PRIVATE
    "${CMAKE_CURRENT_SOURCE_DIR}/openbsd-compat")

# ── libssh.a (LIBOPENSSH_OBJS + XMSS + LIBSSH_OBJS) ─────────────────────────
set(_libssh_names
    ssh_api ssherr sshbuf sshkey sshbuf-getput-basic sshbuf-misc
    sshbuf-getput-crypto krl bitmap
    ssh-xmss sshkey-xmss xmss_commons xmss_fast xmss_hash xmss_hash_address
    xmss_wots
    authfd authfile canohost channels cipher cipher-aes cipher-aesctr cleanup
    compat fatal hostfile log match moduli nchan packet readpass ttymodes
    xmalloc addr addrmatch atomicio dispatch mac misc utf8 monitor_fdpass
    rijndael ssh-dss ssh-ecdsa ssh-ecdsa-sk ssh-ed25519-sk ssh-rsa dh msg
    progressmeter dns entropy gss-genr umac umac128 ssh-pkcs11
    smult_curve25519_ref poly1305 chacha cipher-chachapoly
    cipher-chachapoly-libcrypto ssh-ed25519 digest-openssl digest-libc hmac
    ed25519 hash kex kex-names kexdh kexgex kexecdh kexc25519 kexgexc kexgexs
    kexsntrup761x25519 kexmlkem768x25519 sntrup761 kexgen sftp-realpath
    platform-pledge platform-tracing platform-misc sshbuf-io
)
set(_libssh_src "")
foreach(_n ${_libssh_names})
    list(APPEND _libssh_src "${_n}.c")
endforeach()
add_library(libssh STATIC ${_libssh_src})
set_target_properties(libssh PROPERTIES OUTPUT_NAME ssh)

# libssh and openbsd-compat are mutually referential; rescan as a group so the
# static-archive symbol resolution matches `-lssh -lopenbsd-compat -lssh ...`.
set(SSH_LIBGROUP "$<LINK_GROUP:RESCAN,libssh,openbsd-compat>")

set(SKOBJS ssh-sk-client.c)

# ── Per-target object lists (Makefile.in) and install destinations ──────────
#   add_openssh_exe(<exe> <dest:bin|sbin|libexec> <extra-libs> <sources...>)
function(add_openssh_exe _exe _dest _extralibs)
    add_executable(${_exe} ${ARGN})
    target_link_libraries(${_exe} PRIVATE ${SSH_LIBGROUP} z ${_extralibs})
    install(TARGETS ${_exe} RUNTIME DESTINATION ${_dest})
endfunction()

add_openssh_exe(ssh bin ""
    ssh.c readconf.c clientloop.c sshtty.c sshconnect.c sshconnect2.c mux.c
    ${SKOBJS})

add_openssh_exe(sshd sbin "crypt"
    sshd.c platform-listen.c servconf.c sshpty.c srclimit.c groupaccess.c
    auth2-methods.c dns.c fatal.c compat.c utf8.c authfd.c canohost.c ${SKOBJS})

add_openssh_exe(sshd-session libexec "crypt"
    sshd-session.c auth-rhosts.c auth-passwd.c audit.c audit-bsm.c audit-linux.c
    platform.c sshpty.c sshlogin.c servconf.c serverloop.c auth.c auth2.c
    auth2-methods.c auth-options.c session.c auth2-chall.c groupaccess.c
    auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c auth2-none.c auth2-passwd.c
    auth2-pubkey.c auth2-pubkeyfile.c monitor.c monitor_wrap.c auth-krb5.c
    auth2-gss.c gss-serv.c gss-serv-krb5.c loginrec.c auth-pam.c auth-shadow.c
    auth-sia.c sftp-server.c sftp-common.c sandbox-null.c sandbox-rlimit.c
    sandbox-systrace.c sandbox-darwin.c sandbox-seccomp-filter.c
    sandbox-capsicum.c sandbox-pledge.c sandbox-solaris.c uidswap.c ${SKOBJS})

add_openssh_exe(scp bin ""
    scp.c progressmeter.c sftp-common.c sftp-client.c sftp-glob.c)

add_openssh_exe(ssh-add bin ""
    ssh-add.c ${SKOBJS})

add_openssh_exe(ssh-agent bin ""
    ssh-agent.c ssh-pkcs11-client.c ${SKOBJS})

add_openssh_exe(ssh-keygen bin ""
    ssh-keygen.c sshsig.c ${SKOBJS})

add_openssh_exe(ssh-keysign libexec ""
    ssh-keysign.c readconf.c uidswap.c ${SKOBJS})

add_openssh_exe(ssh-pkcs11-helper libexec ""
    ssh-pkcs11-helper.c ssh-pkcs11.c ${SKOBJS})

add_openssh_exe(ssh-sk-helper libexec ""
    ssh-sk-helper.c ssh-sk.c sk-usbhid.c)

add_openssh_exe(ssh-keyscan bin ""
    ssh-keyscan.c ${SKOBJS})

add_openssh_exe(sftp-server libexec ""
    sftp-common.c sftp-server.c sftp-server-main.c)

add_openssh_exe(sftp bin ""
    sftp.c sftp-usergroup.c progressmeter.c sftp-common.c sftp-client.c
    sftp-glob.c)
