# ════════════════════════════════════════════════════════════════════════════
# GNU-free CMake build for e2fsprogs 1.47.x  (FenrirOS)
#
# Replaces e2fsprogs' recursive GNU-make / MCONFIG build with a single CMake
# project that drives the musl/LLVM toolchain through ninja. It builds the core
# ext2/3/4 userspace (mke2fs, e2fsck, tune2fs, dumpe2fs, resize2fs, badblocks,
# e2label, e2image, e2undo, blkid, filefrag, logsave, chattr, lsattr,
# mklost+found) plus the static support libraries.
#
# PREREQUISITE: e2fsprogs' own ./configure must have been run ONCE in-tree
# (the build function does this with CC=musl-clang). configure is a POSIX-sh
# probe that produces the headers CMake cannot: lib/config.h, lib/dirpaths.h,
# lib/ext2fs/ext2_types.h, lib/blkid/blkid_types.h, lib/uuid/uuid_types.h and
# the lib/et/compile_et script. This file then regenerates the four derived
# sources the Makefiles would build (ext2_err, prof_err, crc32c_table.h,
# default_profile.c) and compiles everything.
#
# Recommended configure flags (set by the build function):
#   --without-libarchive --disable-nls --disable-fuse2fs
# ════════════════════════════════════════════════════════════════════════════
cmake_minimum_required(VERSION 3.24)   # LINK_GROUP:RESCAN for circular static libs
project(e2fsprogs C)

set(E2FSPROGS_VERSION "1.47.4" CACHE STRING "e2fsprogs version")
set(S "${CMAKE_CURRENT_SOURCE_DIR}")
set(B "${CMAKE_CURRENT_BINARY_DIR}")

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

# config.h carries every feature macro the sources need; every .c does
# `#include "config.h"` first, so -DHAVE_CONFIG_H is the only define we add.
add_compile_definitions(HAVE_CONFIG_H)
# e2fsprogs is warning-clean only on glibc; on musl/clang we keep warnings
# non-fatal so a stray -W doesn't abort the build.
add_compile_options(-O2 -Wno-error)

# ── Sanity: configure must have run ──────────────────────────────────────────
if(NOT EXISTS "${S}/lib/config.h")
    message(FATAL_ERROR
        "lib/config.h missing -- run e2fsprogs ./configure before cmake")
endif()

find_program(AWK_EXE NAMES awk gawk mawk REQUIRED)

# compile_et(et_file, work_dir) -- reproduce lib/et/compile_et without needing
# the substituted wrapper script: it is just two awk passes (et_h.awk / et_c.awk)
# over the .et table that emit <base>.h and <base>.c next to it.
function(compile_et _et _dir)
    get_filename_component(_base "${_et}" NAME_WE)
    execute_process(
        COMMAND "${AWK_EXE}" -f "${S}/lib/et/et_h.awk"
                "outfile=${_base}.h" "outfn=${_base}.h" "${_et}"
        WORKING_DIRECTORY "${_dir}" RESULT_VARIABLE _r)
    if(_r)
        message(FATAL_ERROR "et_h.awk on ${_et} failed: ${_r}")
    endif()
    execute_process(
        COMMAND "${AWK_EXE}" -f "${S}/lib/et/et_c.awk"
                "outfile=${_base}.c" "outfn=${_base}.c" "${_et}"
        WORKING_DIRECTORY "${_dir}" RESULT_VARIABLE _r)
    if(_r)
        message(FATAL_ERROR "et_c.awk on ${_et} failed: ${_r}")
    endif()
endfunction()

# Header search: -Ilib resolves "config.h", <et/com_err.h>, "ext2fs/ext2fs.h",
# "blkid/blkid.h", "uuid/uuid.h", "support/*.h"; same-dir headers (ext2_err.h,
# ext2_types.h, ...) resolve relative to their including file automatically.
include_directories("${S}/lib" "${S}")

# ════════════════════════════════════════════════════════════════════════════
# Generated sources (mirror the Makefile.in rules)
# ════════════════════════════════════════════════════════════════════════════

# 0) lib/dirpaths.h  --  the Makefile substitutes @datadir@/@root_sysconfdir@
# from dirpaths.h.in (config.status does NOT create it). Resolve them against
# the install prefix so config.h's `#include <dirpaths.h>` works.
if(NOT EXISTS "${S}/lib/dirpaths.h")
    set(datadir         "${CMAKE_INSTALL_PREFIX}/share")
    set(root_sysconfdir "${CMAKE_INSTALL_PREFIX}/etc")
    configure_file("${S}/lib/dirpaths.h.in" "${S}/lib/dirpaths.h" @ONLY)
endif()

# 0b) Public headers uuid/uuid.h and blkid/blkid.h are plain copies of their
# .in templates (the Makefile rule is just `cp`); config.status leaves them out.
if(NOT EXISTS "${S}/lib/uuid/uuid.h")
    configure_file("${S}/lib/uuid/uuid.h.in" "${S}/lib/uuid/uuid.h" COPYONLY)
endif()
if(NOT EXISTS "${S}/lib/blkid/blkid.h")
    configure_file("${S}/lib/blkid/blkid.h.in" "${S}/lib/blkid/blkid.h" COPYONLY)
endif()

# 1) lib/ext2fs/ext2_err.{c,h}  --  compile_et on the versioned error table.
if(NOT EXISTS "${S}/lib/ext2fs/ext2_err.c")
    configure_file("${S}/lib/ext2fs/ext2_err.et.in"
                   "${S}/lib/ext2fs/ext2_err.et" @ONLY)
    compile_et("${S}/lib/ext2fs/ext2_err.et" "${S}/lib/ext2fs")
endif()

# 2) lib/support/prof_err.{c,h}  --  compile_et on the profile error table.
if(NOT EXISTS "${S}/lib/support/prof_err.c")
    compile_et("${S}/lib/support/prof_err.et" "${S}/lib/support")
endif()

# 3) lib/ext2fs/crc32c_table.h  --  built natively (host == target x86_64).
if(NOT EXISTS "${S}/lib/ext2fs/crc32c_table.h")
    execute_process(
        COMMAND "${CMAKE_C_COMPILER}" -I "${S}/lib/ext2fs" -I "${S}/lib"
                "${S}/lib/ext2fs/gen_crc32ctable.c" -o "${B}/gen_crc32ctable"
        RESULT_VARIABLE _r)
    if(_r)
        message(FATAL_ERROR "compiling gen_crc32ctable failed: ${_r}")
    endif()
    execute_process(
        COMMAND "${B}/gen_crc32ctable"
        OUTPUT_FILE "${S}/lib/ext2fs/crc32c_table.h"
        RESULT_VARIABLE _r)
    if(_r)
        message(FATAL_ERROR "gen_crc32ctable run failed: ${_r}")
    endif()
endif()

# 4) misc/default_profile.c  --  awk transform of the stock mke2fs.conf.
if(NOT EXISTS "${S}/misc/default_profile.c")
    execute_process(
        COMMAND "${AWK_EXE}" -f "${S}/misc/profile-to-c.awk"
        INPUT_FILE  "${S}/misc/mke2fs.conf.in"
        OUTPUT_FILE "${S}/misc/default_profile.c"
        RESULT_VARIABLE _r)
    if(_r)
        message(FATAL_ERROR "profile-to-c.awk failed: ${_r}")
    endif()
endif()

# ════════════════════════════════════════════════════════════════════════════
# Static libraries  (object lists transcribed from each lib/*/Makefile.in OBJS)
# ════════════════════════════════════════════════════════════════════════════

# libcom_err  (lib/et)
add_library(com_err STATIC
    lib/et/error_message.c lib/et/et_name.c lib/et/init_et.c
    lib/et/com_err.c       lib/et/com_right.c)

# libe2p  (lib/e2p)
add_library(e2p STATIC
    lib/e2p/feature.c   lib/e2p/fgetflags.c    lib/e2p/fsetflags.c
    lib/e2p/fgetversion.c lib/e2p/fsetversion.c lib/e2p/getflags.c
    lib/e2p/getversion.c lib/e2p/hashstr.c     lib/e2p/iod.c
    lib/e2p/ls.c        lib/e2p/ljs.c          lib/e2p/mntopts.c
    lib/e2p/parse_num.c lib/e2p/pe.c           lib/e2p/pf.c
    lib/e2p/ps.c        lib/e2p/setflags.c     lib/e2p/setversion.c
    lib/e2p/uuid.c      lib/e2p/ostype.c       lib/e2p/percent.c
    lib/e2p/crypto_mode.c lib/e2p/fgetproject.c lib/e2p/fsetproject.c
    lib/e2p/encoding.c  lib/e2p/errcode.c)

# libuuid  (lib/uuid)
add_library(uuid STATIC
    lib/uuid/clear.c   lib/uuid/compare.c lib/uuid/copy.c
    lib/uuid/gen_uuid.c lib/uuid/isnull.c lib/uuid/pack.c
    lib/uuid/parse.c   lib/uuid/unpack.c  lib/uuid/unparse.c
    lib/uuid/uuid_time.c)

# libblkid  (lib/blkid)
add_library(blkid STATIC
    lib/blkid/cache.c   lib/blkid/dev.c     lib/blkid/devname.c
    lib/blkid/devno.c   lib/blkid/getsize.c lib/blkid/llseek.c
    lib/blkid/probe.c   lib/blkid/read.c    lib/blkid/resolve.c
    lib/blkid/save.c    lib/blkid/tag.c     lib/blkid/version.c)
target_link_libraries(blkid PUBLIC uuid)

# libext2fs  (lib/ext2fs)  -- includes the *_LIB_OBJS used by resize2fs/e2image
# (dupfs, imager) and debugfs helpers, plus tdb, unix_io and the sparse_io stub.
add_library(ext2fs STATIC
    lib/ext2fs/ext2_err.c
    lib/ext2fs/dupfs.c        lib/ext2fs/imager.c
    lib/ext2fs/bb_compat.c    lib/ext2fs/inode_io.c lib/ext2fs/write_bb_file.c
    lib/ext2fs/test_io.c
    lib/ext2fs/alloc.c        lib/ext2fs/alloc_sb.c lib/ext2fs/alloc_stats.c
    lib/ext2fs/alloc_tables.c lib/ext2fs/atexit.c   lib/ext2fs/badblocks.c
    lib/ext2fs/bb_inode.c     lib/ext2fs/bitmaps.c  lib/ext2fs/bitops.c
    lib/ext2fs/blkmap64_ba.c  lib/ext2fs/blkmap64_rb.c lib/ext2fs/blknum.c
    lib/ext2fs/block.c        lib/ext2fs/bmap.c     lib/ext2fs/check_desc.c
    lib/ext2fs/closefs.c      lib/ext2fs/crc16.c    lib/ext2fs/crc32c.c
    lib/ext2fs/csum.c         lib/ext2fs/dblist.c   lib/ext2fs/dblist_dir.c
    lib/ext2fs/dirblock.c     lib/ext2fs/dirhash.c  lib/ext2fs/dir_iterate.c
    lib/ext2fs/expanddir.c    lib/ext2fs/ext_attr.c lib/ext2fs/extent.c
    lib/ext2fs/fallocate.c    lib/ext2fs/fileio.c   lib/ext2fs/finddev.c
    lib/ext2fs/flushb.c       lib/ext2fs/freefs.c   lib/ext2fs/gen_bitmap.c
    lib/ext2fs/gen_bitmap64.c lib/ext2fs/get_num_dirs.c lib/ext2fs/get_pathname.c
    lib/ext2fs/getenv.c       lib/ext2fs/getsize.c  lib/ext2fs/getsectsize.c
    lib/ext2fs/hashmap.c      lib/ext2fs/i_block.c  lib/ext2fs/icount.c
    lib/ext2fs/ind_block.c    lib/ext2fs/initialize.c lib/ext2fs/inline.c
    lib/ext2fs/inline_data.c  lib/ext2fs/inode.c    lib/ext2fs/io_manager.c
    lib/ext2fs/ismounted.c    lib/ext2fs/link.c     lib/ext2fs/llseek.c
    lib/ext2fs/lookup.c       lib/ext2fs/mkdir.c    lib/ext2fs/mkjournal.c
    lib/ext2fs/mmp.c          lib/ext2fs/namei.c    lib/ext2fs/native.c
    lib/ext2fs/newdir.c       lib/ext2fs/nls_utf8.c lib/ext2fs/openfs.c
    lib/ext2fs/orphan.c       lib/ext2fs/progress.c lib/ext2fs/punch.c
    lib/ext2fs/qcow2.c        lib/ext2fs/read_bb.c  lib/ext2fs/read_bb_file.c
    lib/ext2fs/res_gdt.c      lib/ext2fs/rw_bitmaps.c lib/ext2fs/sha512.c
    lib/ext2fs/swapfs.c       lib/ext2fs/symlink.c  lib/ext2fs/tdb.c
    lib/ext2fs/undo_io.c      lib/ext2fs/unix_io.c  lib/ext2fs/sparse_io.c
    lib/ext2fs/unlink.c       lib/ext2fs/valid_blk.c lib/ext2fs/version.c
    lib/ext2fs/rbtree.c)
target_link_libraries(ext2fs PUBLIC com_err)

# libsupport  (lib/support)
add_library(support STATIC
    lib/support/cstring.c   lib/support/mkquota.c
    lib/support/plausible.c lib/support/profile.c   lib/support/parse_qtype.c
    lib/support/print_fs_flags.c lib/support/profile_helpers.c
    lib/support/prof_err.c  lib/support/quotaio.c   lib/support/quotaio_v2.c
    lib/support/quotaio_tree.c lib/support/dict.c
    lib/support/devname.c)
target_link_libraries(support PUBLIC ext2fs e2p blkid com_err)

# Circular static-archive group: lets the linker rescan until all refs resolve.
set(E2FS_LIBS "$<LINK_GROUP:RESCAN,ext2fs,support,e2p,blkid,uuid,com_err>")

# ════════════════════════════════════════════════════════════════════════════
# Programs  (object lists from misc/, e2fsck/, resize/ Makefile.in)
# ════════════════════════════════════════════════════════════════════════════

# mke2fs (+ create_inode_libarchive.c is an empty stub without --with-libarchive)
add_executable(mke2fs
    misc/mke2fs.c misc/util.c misc/default_profile.c misc/mk_hugefiles.c
    misc/create_inode.c misc/create_inode_libarchive.c)
target_link_libraries(mke2fs PRIVATE ${E2FS_LIBS})

# e2fsck
add_executable(e2fsck
    e2fsck/unix.c    e2fsck/e2fsck.c  e2fsck/super.c   e2fsck/pass1.c
    e2fsck/pass1b.c  e2fsck/pass2.c   e2fsck/pass3.c   e2fsck/pass4.c
    e2fsck/pass5.c   e2fsck/journal.c e2fsck/badblocks.c e2fsck/util.c
    e2fsck/dirinfo.c e2fsck/dx_dirinfo.c e2fsck/ehandler.c e2fsck/problem.c
    e2fsck/message.c e2fsck/quota.c   e2fsck/recovery.c e2fsck/region.c
    e2fsck/revoke.c  e2fsck/ea_refcount.c e2fsck/rehash.c e2fsck/logfile.c
    e2fsck/sigcatcher.c e2fsck/readahead.c e2fsck/extents.c
    e2fsck/encrypted_files.c)
target_include_directories(e2fsck PRIVATE "${S}/e2fsck")
target_link_libraries(e2fsck PRIVATE ${E2FS_LIBS})

# tune2fs -- its journal/recovery/revoke objects come from debugfs/ and e2fsck/
# and must be built with -DDEBUGFS and -I e2fsck (JOURNAL_CFLAGS). Isolate them
# in an OBJECT library so the define/include don't leak into tune2fs.c/util.c.
add_library(tune2fs_journal OBJECT
    debugfs/journal.c e2fsck/recovery.c e2fsck/revoke.c)
target_compile_definitions(tune2fs_journal PRIVATE DEBUGFS)
target_include_directories(tune2fs_journal PRIVATE "${S}/e2fsck" "${S}/misc")

add_executable(tune2fs
    misc/tune2fs.c misc/util.c $<TARGET_OBJECTS:tune2fs_journal>)
target_include_directories(tune2fs PRIVATE "${S}/e2fsck" "${S}/misc")
target_link_libraries(tune2fs PRIVATE ${E2FS_LIBS})

# Small single-source tools sharing the same lib group.
foreach(_p dumpe2fs badblocks e2image e2undo filefrag)
    add_executable(${_p} misc/${_p}.c)
    target_link_libraries(${_p} PRIVATE ${E2FS_LIBS})
endforeach()

# blkid CLI
add_executable(blkid_prog misc/blkid.c)
set_target_properties(blkid_prog PROPERTIES OUTPUT_NAME blkid)
target_link_libraries(blkid_prog PRIVATE ${E2FS_LIBS})

# chattr / lsattr only need e2p (+com_err).
add_executable(chattr misc/chattr.c)
add_executable(lsattr misc/lsattr.c)
target_link_libraries(chattr PRIVATE e2p com_err)
target_link_libraries(lsattr PRIVATE e2p com_err)

# mklost+found is self-contained.
add_executable(mklost_found misc/mklost+found.c)
set_target_properties(mklost_found PROPERTIES OUTPUT_NAME "mklost+found")

# logsave is self-contained (no libraries).
add_executable(logsave misc/logsave.c)

# resize2fs
add_executable(resize2fs
    resize/extent.c resize/resize2fs.c resize/main.c
    resize/online.c resize/resource_track.c resize/sim_progress.c)
target_include_directories(resize2fs PRIVATE "${S}/resize")
target_link_libraries(resize2fs PRIVATE ${E2FS_LIBS})

# ════════════════════════════════════════════════════════════════════════════
# Install  (sbin for fs tools, bin for the attribute utilities)
# ════════════════════════════════════════════════════════════════════════════
install(TARGETS mke2fs e2fsck tune2fs dumpe2fs badblocks resize2fs
                e2image e2undo blkid_prog mklost_found logsave
        RUNTIME DESTINATION sbin)
install(TARGETS filefrag chattr lsattr
        RUNTIME DESTINATION bin)

# Filesystem-variant symlinks (mke2fs/e2fsck dispatch on argv[0]); e2label is a
# symlink to tune2fs (it dispatches on argv[0]), not a separate binary.
install(CODE "
    set(_sb \"\${CMAKE_INSTALL_PREFIX}/sbin\")
    foreach(_v ext2 ext3 ext4)
        file(CREATE_LINK mke2fs \"\${_sb}/mkfs.\${_v}\" SYMBOLIC)
        file(CREATE_LINK e2fsck \"\${_sb}/fsck.\${_v}\" SYMBOLIC)
    endforeach()
    file(CREATE_LINK tune2fs \"\${_sb}/e2label\" SYMBOLIC)
")

# Expose the static UUID library + its header so external GNU-free tools (e.g.
# gptfdisk/sgdisk) can link the e2fsprogs libuuid instead of util-linux. Static
# only — a .a is never loaded at runtime, so it cannot poison glibc host tools.
install(TARGETS uuid ARCHIVE DESTINATION lib)
install(FILES "${S}/lib/uuid/uuid.h" DESTINATION include/uuid)
