# ════════════════════════════════════════════════════════════════════════════
# 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 .h and .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", , "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 ` 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 "$")
# ════════════════════════════════════════════════════════════════════════════
# 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_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)