# ════════════════════════════════════════════════════════════════════════════
# GNU-free CMake build for dosfstools (FenrirOS)
#
# Replaces dosfstools' autotools (configure.ac/Makefile.am) build with CMake so
# the FAT tools (mkfs.fat, fsck.fat, fatlabel) build with the musl/LLVM
# toolchain driven by ninja — no autoreconf/automake/GNU-make required.
#
# dosfstools does NOT use a config.h: its configure.ac has no AC_CONFIG_HEADERS,
# so the autoconf build passes the feature macros via -D on the compile line
# (@DEFS@). We reproduce that here by probing the platform with CMake's
# check_* modules and adding the resulting -D HAVE_* defines globally. version.h
# (the only generated header the sources #include) is produced via configure_file.
# ════════════════════════════════════════════════════════════════════════════
cmake_minimum_required(VERSION 3.13)
project(dosfstools C)

include(CheckIncludeFile)
include(CheckSymbolExists)

# dosfstools is GPLv3; version string comes from configure.ac's AC_INIT.
set(PACKAGE_VERSION "4.2" CACHE STRING "dosfstools version")
string(TIMESTAMP RELEASE_DATE "%Y-%m-%d")

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release")
endif()

# vasprintf/getmntent live behind _GNU_SOURCE in musl; turn it on everywhere
# (matches the autotools build's AC_USE_SYSTEM_EXTENSIONS).
add_compile_definitions(_GNU_SOURCE)
# ICONV_CONST is normally emitted by autoconf's AC_FUNC_ICONV: it is `const`
# when iconv()'s 2nd argument is `const char **`, else empty. musl declares it
# as plain `char **`, so define it empty (charconv.c references it unguarded).
add_compile_definitions(ICONV_CONST=)
add_compile_options(-O2 -Wall -Wextra -Wno-sign-compare -Wno-missing-field-initializers)

# ── Platform feature probes → -D HAVE_* (mirrors configure.ac's checks) ──────
check_include_file("endian.h"                  HAVE_ENDIAN_H)
check_include_file("sys/endian.h"              HAVE_SYS_ENDIAN_H)
check_include_file("libkern/OSByteOrder.h"     HAVE_LIBKERN_OSBYTEORDER_H)
check_include_file("sys/sysmacros.h"           HAVE_SYS_SYSMACROS_H)
check_include_file("sys/queue.h"               HAVE_SYS_QUEUE_H)
check_include_file("sys/disk.h"                HAVE_SYS_DISK_H)
check_include_file("sys/disklabel.h"           HAVE_SYS_DISKLABEL_H)
check_include_file("linux/loop.h"              HAVE_LINUX_LOOP_H)
check_include_file("linux/fd.h"                HAVE_LINUX_FD_H)
check_include_file("linux/hdreg.h"             HAVE_LINUX_HDREG_H)
check_include_file("iconv.h"                   HAVE_ICONV)

check_symbol_exists(vasprintf  "stdio.h"        HAVE_VASPRINTF)
check_symbol_exists(getmntent  "mntent.h"       HAVE_DECL_GETMNTENT)
check_symbol_exists(getmntinfo "sys/mount.h"    HAVE_DECL_GETMNTINFO)

# Emit each positive probe as the -DHAVE_X=1 the sources expect; the two
# HAVE_DECL_* macros are referenced with `#if`, so define them to 0 when absent.
foreach(_m HAVE_ENDIAN_H HAVE_SYS_ENDIAN_H HAVE_LIBKERN_OSBYTEORDER_H
           HAVE_SYS_SYSMACROS_H HAVE_SYS_QUEUE_H HAVE_SYS_DISK_H
           HAVE_SYS_DISKLABEL_H HAVE_LINUX_LOOP_H HAVE_LINUX_FD_H
           HAVE_LINUX_HDREG_H HAVE_ICONV HAVE_VASPRINTF)
    if(${_m})
        add_compile_definitions(${_m}=1)
    endif()
endforeach()
if(HAVE_DECL_GETMNTENT)
    add_compile_definitions(HAVE_DECL_GETMNTENT=1)
else()
    add_compile_definitions(HAVE_DECL_GETMNTENT=0)
endif()
if(HAVE_DECL_GETMNTINFO)
    add_compile_definitions(HAVE_DECL_GETMNTINFO=1)
else()
    add_compile_definitions(HAVE_DECL_GETMNTINFO=0)
endif()

# version.h --- the one header the sources generate (VERSION / VERSION_DATE).
configure_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in"
    "${CMAKE_CURRENT_BINARY_DIR}/version.h"
    @ONLY)

include_directories(
    "${CMAKE_CURRENT_SOURCE_DIR}/src"
    "${CMAKE_CURRENT_BINARY_DIR}")   # generated version.h

# ── Source groups (mirror src/Makefile.am) ──────────────────────────────────
set(_charconv      src/charconv.c)
set(_fscklabel
    src/boot.c src/common.c src/fat.c src/io.c ${_charconv})
set(_devinfo
    src/device_info.c src/blkdev/blkdev.c src/blkdev/linux_version.c)

# fsck.fat
add_executable(fsck.fat
    src/check.c src/file.c src/fsck.fat.c src/lfn.c ${_fscklabel})

# fatlabel
add_executable(fatlabel
    src/fatlabel.c ${_fscklabel})

# mkfs.fat (needs the blkdev/ headers and device_info)
add_executable(mkfs.fat
    src/mkfs.fat.c src/common.c ${_charconv} ${_devinfo})
target_include_directories(mkfs.fat PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/blkdev")

# musl provides iconv in libc, so no separate -liconv is required.

install(TARGETS fsck.fat fatlabel mkfs.fat RUNTIME DESTINATION sbin)

# Traditional compatibility names (configure.ac COMPAT_SYMLINKS / install-hook).
install(CODE "
    set(_sb \"\${CMAKE_INSTALL_PREFIX}/sbin\")
    file(CREATE_LINK fatlabel \"\${_sb}/dosfslabel\" SYMBOLIC)
    file(CREATE_LINK fsck.fat \"\${_sb}/dosfsck\"   SYMBOLIC)
    file(CREATE_LINK fsck.fat \"\${_sb}/fsck.msdos\" SYMBOLIC)
    file(CREATE_LINK fsck.fat \"\${_sb}/fsck.vfat\"  SYMBOLIC)
    file(CREATE_LINK mkfs.fat \"\${_sb}/mkdosfs\"    SYMBOLIC)
    file(CREATE_LINK mkfs.fat \"\${_sb}/mkfs.msdos\" SYMBOLIC)
    file(CREATE_LINK mkfs.fat \"\${_sb}/mkfs.vfat\"  SYMBOLIC)
")
