Skip to content

Commit bcd74c6

Browse files
committed
Add a CMake build alongside autotools
CMake >= 3.18, coexisting with the autotools build; both read the package version from the shared VERSION file. Linux is fully wired (libltfs, the ltfs/mkltfs/ltfsck executables, the sg/file/itdtimg tape backends, both I/O schedulers, both key managers); macOS builds the iokit tape backend; the FreeBSD/NetBSD backends are skipped with a notice and remain autotools-only. The build targets libfuse 2, like autotools; the FUSE 3 selection arrives with the FUSE 3 port. Notable differences from the autotools implementation: - ICU is found through CMake's ICU module instead of the icu-config tool that ICU removed in 2018, and ICU6x is defined automatically for ICU >= 60. libxml2 uses find_package(LibXml2); fuse/uuid/ net-snmp have no CMake packages and use pkg-config imported targets. - config.h is generated from cmake/config.h.in, covering the subset of macros the sources use; feature toggles are passed on the command line, as in the autotools build. - The message bundles are compiled by the same shared messages/make_message_src.sh, invoked with explicit source/output directories and the discovered genrb/pkgdata. - The per-backend source symlinks and the CRC_OPTIMIZE compile rule are replaced by direct source paths and one SSE4.2-flagged OBJECT library linked into every tape backend. - Plugins are MODULE libraries named exactly as ltfs.conf expects (libtape-sg.so etc.), installed to <libdir>/ltfs. - ctest registers tests/t/*.sh by glob via the shared harness (SKIP_RETURN_CODE 77 outside Linux/fuse hosts), so the build works with or without the integration test suite. A CI workflow configures, builds, and runs ctest on ubuntu. Verified in an Ubuntu VM: the build and all 14 tests pass via ctest with the test suite present; the install tree matches autotools (bin, lib, lib/ltfs plugins, etc/ltfs.conf{,.local}, pkgconfig, man, headers); ltfs --version reports the identical version string; the autotools build and make check stay green. On macOS 26 (arm64, macFUSE SDK) the CMake build produces libltfs, the executables, and all plugins including libtape-iokit.so.
1 parent 4637be2 commit bcd74c6

20 files changed

Lines changed: 688 additions & 1 deletion

File tree

.github/workflows/cmake.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: CMake Build
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
name: Build and test with CMake
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- name: Set up Git repository
12+
uses: actions/checkout@v6
13+
with:
14+
submodules: recursive
15+
16+
- name: Install dependencies
17+
run: |
18+
sudo apt-get -q update
19+
sudo apt-get -q -y install --no-install-recommends \
20+
build-essential cmake pkg-config \
21+
icu-devtools libicu-dev libxml2-dev uuid-dev libsnmp-dev \
22+
libfuse-dev attr
23+
24+
- name: Build
25+
run: |
26+
cmake -S . -B build
27+
cmake --build build -j"$(nproc)"
28+
29+
- name: Run tests
30+
run: ctest --test-dir build --output-on-failure

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ aclocal.m4
44
autom4te.cache/
55
build-aux/
66
m4/
7-
config.h.in
7+
/config.h.in
88
configure
99
# Files generated by ./configure
1010
config.h

CMakeLists.txt

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#
2+
# CMake build for LTFS. Coexists with the autotools build; both are
3+
# maintained. The package version is read from the top-level VERSION file
4+
# (the single source of truth shared with configure.ac).
5+
#
6+
cmake_minimum_required(VERSION 3.18)
7+
8+
# --- Version (single source of truth: ./VERSION) -----------------------------
9+
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" _ltfs_ver_lines)
10+
list(GET _ltfs_ver_lines 0 LTFS_VERSION_NUM)
11+
list(LENGTH _ltfs_ver_lines _ltfs_ver_n)
12+
set(LTFS_VERSION_SUFFIX "")
13+
if(_ltfs_ver_n GREATER 1)
14+
list(GET _ltfs_ver_lines 1 LTFS_VERSION_SUFFIX)
15+
endif()
16+
if(LTFS_VERSION_SUFFIX STREQUAL "")
17+
set(LTFS_VERSION_FULL "${LTFS_VERSION_NUM}")
18+
else()
19+
set(LTFS_VERSION_FULL "${LTFS_VERSION_NUM} ${LTFS_VERSION_SUFFIX}")
20+
endif()
21+
22+
project(ltfs VERSION ${LTFS_VERSION_NUM} LANGUAGES C)
23+
24+
set(LTFS_PACKAGE_NAME "LTFS")
25+
set(LTFS_PACKAGE_TARNAME "ltfs")
26+
set(LTFS_BUGREPORT "IBM corporation.")
27+
28+
set(CMAKE_C_STANDARD 99)
29+
set(CMAKE_C_STANDARD_REQUIRED ON)
30+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
31+
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE)
32+
endif()
33+
34+
include(GNUInstallDirs)
35+
include(CheckIncludeFile)
36+
include(CheckSymbolExists)
37+
include(CheckTypeSize)
38+
include(CheckCCompilerFlag)
39+
40+
# --- Options (mirror the autotools --enable/--with switches) ------------------
41+
option(LTFS_ENABLE_SNMP "Build with SNMP support" ON)
42+
option(LTFS_ENABLE_LINTAPE "Build the IBM lin_tape backend (Linux only)" OFF)
43+
option(LTFS_SUPPORT_BUGGY_IFS "Work around buggy tape interfaces" OFF)
44+
option(LTFS_LIVELINK "Enable livelink symlink mode" ON)
45+
option(LTFS_MESSAGE_CHECK "Compile in message-checking mode" OFF)
46+
option(LTFS_WERROR "Treat warnings as errors" OFF)
47+
option(LTFS_DEBUG "Compile with extra debugging output" OFF)
48+
49+
# New locking defaults ON for NetBSD (its old locking is broken), OFF elsewhere.
50+
if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
51+
option(LTFS_NEW_LOCKING "Use the new locking implementation" ON)
52+
else()
53+
option(LTFS_NEW_LOCKING "Use the new locking implementation" OFF)
54+
endif()
55+
56+
# Default plugin selection, matching configure.ac.
57+
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
58+
set(_default_tape "sg")
59+
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
60+
set(_default_tape "iokit")
61+
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
62+
set(_default_tape "cam")
63+
elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
64+
set(_default_tape "scsipi-ibmtape")
65+
endif()
66+
set(DEFAULT_TAPE "${_default_tape}" CACHE STRING "Default tape backend plugin")
67+
set(DEFAULT_IOSCHED "unified" CACHE STRING "Default I/O scheduler plugin")
68+
set(DEFAULT_KMI "none" CACHE STRING "Default key manager plugin")
69+
70+
# --- Dependencies ------------------------------------------------------------
71+
# find_package is used wherever a CMake package exists (LibXml2, ICU, Threads).
72+
# libfuse, libuuid, and net-snmp ship no CMake config; pkg-config is their
73+
# canonical interface, wrapped as IMPORTED_TARGETs (PkgConfig::*).
74+
find_package(PkgConfig REQUIRED)
75+
76+
pkg_check_modules(FUSE REQUIRED IMPORTED_TARGET fuse>=2.6.0)
77+
78+
find_package(LibXml2 2.6.16 REQUIRED)
79+
80+
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
81+
pkg_check_modules(UUID REQUIRED IMPORTED_TARGET uuid>=1.6)
82+
else()
83+
pkg_check_modules(UUID REQUIRED IMPORTED_TARGET uuid>=1.36)
84+
endif()
85+
86+
# ICU via the CMake module (replaces the removed icu-config).
87+
find_package(ICU REQUIRED COMPONENTS uc i18n io data)
88+
set(LTFS_ICU6X OFF)
89+
if(ICU_VERSION VERSION_GREATER_EQUAL "60")
90+
set(LTFS_ICU6X ON)
91+
endif()
92+
93+
find_package(Threads REQUIRED)
94+
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
95+
find_library(RT_LIBRARY rt)
96+
endif()
97+
98+
# SNMP (optional).
99+
if(LTFS_ENABLE_SNMP)
100+
pkg_check_modules(NETSNMP IMPORTED_TARGET net-snmp>=5.3)
101+
if(NOT NETSNMP_FOUND)
102+
message(WARNING "net-snmp not found via pkg-config; disabling SNMP")
103+
set(LTFS_ENABLE_SNMP OFF)
104+
endif()
105+
endif()
106+
107+
# Platform-specific libraries (declared for all platforms; used where built).
108+
# backtrace() has a CMake module (libc on glibc, libexecinfo on the BSDs);
109+
# cam/bsdxml/mt and the Apple frameworks ship neither CMake configs nor
110+
# pkg-config files, so find_library is the canonical way to locate them.
111+
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
112+
find_library(CAM_LIBRARY cam REQUIRED)
113+
find_library(BSDXML_LIBRARY bsdxml REQUIRED)
114+
find_library(MT_LIBRARY mt REQUIRED)
115+
find_package(Backtrace REQUIRED)
116+
elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
117+
find_package(Backtrace REQUIRED)
118+
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
119+
find_library(IOKIT_FRAMEWORK IOKit REQUIRED)
120+
find_library(COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
121+
endif()
122+
123+
# ICU host tools for the message bundles.
124+
find_program(GENRB genrb REQUIRED)
125+
find_program(PKGDATA pkgdata REQUIRED)
126+
127+
# --- Feature checks for config.h ---------------------------------------------
128+
check_include_file("sys/sysctl.h" HAVE_SYS_SYSCTL_H)
129+
check_include_file("fcntl.h" HAVE_FCNTL_H)
130+
check_include_file("limits.h" HAVE_LIMITS_H)
131+
check_include_file("stddef.h" HAVE_STDDEF_H)
132+
check_include_file("stdint.h" HAVE_STDINT_H)
133+
check_include_file("stdlib.h" HAVE_STDLIB_H)
134+
check_include_file("string.h" HAVE_STRING_H)
135+
check_include_file("sys/ioctl.h" HAVE_SYS_IOCTL_H)
136+
check_include_file("sys/mount.h" HAVE_SYS_MOUNT_H)
137+
check_include_file("sys/time.h" HAVE_SYS_TIME_H)
138+
check_include_file("termios.h" HAVE_TERMIOS_H)
139+
check_include_file("unistd.h" HAVE_UNISTD_H)
140+
check_type_size("time_t" SIZEOF_TIME_T)
141+
142+
# xmlTextReaderSetup and XML_PARSE_HUGE (need libxml2 flags in scope).
143+
# XML_PARSE_HUGE is an enumerator, not a macro, so it needs a compile test.
144+
include(CheckCSourceCompiles)
145+
set(CMAKE_REQUIRED_INCLUDES ${LIBXML2_INCLUDE_DIRS})
146+
set(CMAKE_REQUIRED_LIBRARIES ${LIBXML2_LIBRARIES})
147+
check_symbol_exists(xmlTextReaderSetup "libxml/xmlreader.h" HAVE_XML_READER_SETUP)
148+
check_c_source_compiles("
149+
#include <libxml/parser.h>
150+
int main(void) { int foo = XML_PARSE_HUGE; (void)foo; return 0; }"
151+
HAVE_XML_PARSE_HUGE)
152+
unset(CMAKE_REQUIRED_INCLUDES)
153+
unset(CMAKE_REQUIRED_LIBRARIES)
154+
155+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in
156+
${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY)
157+
158+
# --- Common compile settings -------------------------------------------------
159+
# Definitions every translation unit gets, mirroring AM_CPPFLAGS/AM_CFLAGS.
160+
add_compile_definitions(
161+
_GNU_SOURCE
162+
LTFS_CONFIG_FILE="${CMAKE_INSTALL_FULL_SYSCONFDIR}/ltfs.conf"
163+
LTFS_BASE_DIR="${CMAKE_INSTALL_PREFIX}"
164+
)
165+
if(LTFS_ENABLE_SNMP)
166+
add_compile_definitions(ENABLE_SNMP)
167+
endif()
168+
if(LTFS_NEW_LOCKING)
169+
add_compile_definitions(USE_NEW_LOCKING)
170+
endif()
171+
if(NOT LTFS_LIVELINK)
172+
add_compile_definitions(POSIXLINK_ONLY)
173+
endif()
174+
if(LTFS_MESSAGE_CHECK)
175+
add_compile_definitions(MSG_CHECK)
176+
endif()
177+
if(LTFS_DEBUG)
178+
add_compile_definitions(DEBUG TRACE)
179+
endif()
180+
if(LTFS_ICU6X)
181+
add_compile_definitions(ICU6x)
182+
endif()
183+
if(LTFS_SUPPORT_BUGGY_IFS AND (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
184+
add_compile_definitions(SUPPORT_BUGGY_IFS)
185+
endif()
186+
187+
# -Wno-unused-parameter: callback signatures (FUSE operations, plugin ops)
188+
# must keep parameters they do not use.
189+
# -Wno-missing-field-initializers: designated initializers of operation and
190+
# option tables intentionally leave the remaining members zeroed.
191+
add_compile_options(-Wall -Wextra -Wno-unused-parameter
192+
-Wno-missing-field-initializers -Wsign-compare -fsigned-char)
193+
if(LTFS_WERROR)
194+
add_compile_options(-Werror -Wno-deprecated-declarations)
195+
endif()
196+
197+
# config.h (binary dir) and the src tree on the include path for every target.
198+
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src)
199+
200+
# CRC files need SSE4.2 on capable x86; detected once, applied to the ltfs_crc
201+
# object library (see src/tape_drivers).
202+
set(LTFS_CRC_OPTIONS "")
203+
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|i[3-6]86")
204+
check_c_compiler_flag("-msse4.2" LTFS_HAVE_MSSE42)
205+
if(LTFS_HAVE_MSSE42)
206+
set(LTFS_CRC_OPTIONS -msse4.2 -D__SSE42__)
207+
endif()
208+
endif()
209+
210+
# Helper modules.
211+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
212+
include(LtfsMessages)
213+
include(LtfsPlugin)
214+
215+
enable_testing()
216+
217+
# --- Subprojects -------------------------------------------------------------
218+
add_subdirectory(messages)
219+
add_subdirectory(src)
220+
add_subdirectory(conf)
221+
add_subdirectory(man)
222+
add_subdirectory(init.d)
223+
add_subdirectory(tests)
224+
225+
# pkg-config file (reuse the autotools template).
226+
set(prefix "${CMAKE_INSTALL_PREFIX}")
227+
set(exec_prefix "\${prefix}")
228+
set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
229+
set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
230+
set(PACKAGE "ltfs")
231+
set(VERSION "${LTFS_VERSION_FULL}")
232+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ltfs.pc.in
233+
${CMAKE_CURRENT_BINARY_DIR}/ltfs.pc @ONLY)
234+
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ltfs.pc
235+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

cmake/LtfsMessages.cmake

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# ICU message-bundle compilation for the CMake build. Reuses the shared
2+
# messages/make_message_src.sh so the genrb/pkgdata logic lives in one place.
3+
4+
# ltfs_add_message_bundle(<name>)
5+
# Compiles messages/<name>/*.txt into <build>/messages/lib<name>_dat.a and
6+
# registers a build target msg_<name>. Link it into a consumer with
7+
# ltfs_link_message().
8+
function(ltfs_add_message_bundle name)
9+
set(_src_dir "${CMAKE_SOURCE_DIR}/messages/${name}")
10+
set(_out_dir "${CMAKE_BINARY_DIR}/messages")
11+
set(_archive "${_out_dir}/lib${name}_dat.a")
12+
file(GLOB _txt "${_src_dir}/*.txt")
13+
14+
add_custom_command(
15+
OUTPUT "${_archive}"
16+
COMMAND ${CMAKE_COMMAND} -E make_directory "${_out_dir}"
17+
COMMAND ${CMAKE_COMMAND} -E env "GENRB=${GENRB}" "PKGDATA=${PKGDATA}"
18+
sh "${CMAKE_SOURCE_DIR}/messages/make_message_src.sh"
19+
"lib${name}_dat.a" "${_src_dir}" "${_out_dir}"
20+
DEPENDS ${_txt} "${CMAKE_SOURCE_DIR}/messages/make_message_src.sh"
21+
COMMENT "Compiling message bundle ${name}"
22+
VERBATIM)
23+
24+
add_custom_target(msg_${name} DEPENDS "${_archive}")
25+
# Record the archive path for ltfs_link_message().
26+
set_property(GLOBAL PROPERTY ltfs_msg_archive_${name} "${_archive}")
27+
endfunction()
28+
29+
# ltfs_link_message(<target> <name>)
30+
# Links message bundle <name> into <target> (as a plain archive input, the
31+
# way the autotools build put it on LDFLAGS) and orders the build after it.
32+
function(ltfs_link_message target name)
33+
get_property(_archive GLOBAL PROPERTY ltfs_msg_archive_${name})
34+
if(NOT _archive)
35+
message(FATAL_ERROR "Unknown message bundle '${name}'")
36+
endif()
37+
target_link_libraries(${target} PRIVATE "${_archive}")
38+
add_dependencies(${target} msg_${name})
39+
endfunction()

cmake/LtfsPlugin.cmake

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Helper for LTFS runtime plugins (tape backends, I/O schedulers, key managers).
2+
# Reproduces the autotools "-module -avoid-version" behavior: an unversioned
3+
# shared module named lib<x>.so, installed under <libdir>/ltfs.
4+
5+
# ltfs_add_plugin(<target>
6+
# SOURCES <files...>
7+
# [MSG <message-bundle-name>]
8+
# [CRC] # link the SSE-optimized CRC objects
9+
# [INCLUDES <dirs...>]
10+
# [DEFINES <defs...>]
11+
# [LIBS <libs...>])
12+
function(ltfs_add_plugin target)
13+
cmake_parse_arguments(P "CRC" "MSG" "SOURCES;INCLUDES;DEFINES;LIBS" ${ARGN})
14+
15+
add_library(${target} MODULE ${P_SOURCES})
16+
set_target_properties(${target} PROPERTIES
17+
PREFIX "" # name is exactly <target>.so, no extra lib prefix
18+
SUFFIX ".so" # plugins are .so even on macOS (loader expects it)
19+
OUTPUT_NAME "${target}")
20+
21+
target_link_libraries(${target} PRIVATE ltfs ${P_LIBS})
22+
if(P_CRC)
23+
target_link_libraries(${target} PRIVATE ltfs_crc)
24+
endif()
25+
if(P_INCLUDES)
26+
target_include_directories(${target} PRIVATE ${P_INCLUDES})
27+
endif()
28+
if(P_DEFINES)
29+
target_compile_definitions(${target} PRIVATE ${P_DEFINES})
30+
endif()
31+
if(P_MSG)
32+
ltfs_link_message(${target} ${P_MSG})
33+
endif()
34+
35+
install(TARGETS ${target} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/ltfs)
36+
endfunction()

cmake/config.h.in

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* config.h generated by CMake. Parity with the autotools config.h for the
2+
* subset of macros the LTFS sources actually use. Feature toggles such as
3+
* HAVE_FUSE3, ICU6x, USE_NEW_LOCKING are passed on the command line (see the
4+
* top-level CMakeLists.txt), exactly as the autotools build does. */
5+
#ifndef LTFS_CMAKE_CONFIG_H
6+
#define LTFS_CMAKE_CONFIG_H
7+
8+
#define PACKAGE "@LTFS_PACKAGE_TARNAME@"
9+
#define PACKAGE_NAME "@LTFS_PACKAGE_NAME@"
10+
#define PACKAGE_TARNAME "@LTFS_PACKAGE_TARNAME@"
11+
#define PACKAGE_VERSION "@LTFS_VERSION_FULL@"
12+
#define PACKAGE_STRING "@LTFS_PACKAGE_NAME@ @LTFS_VERSION_FULL@"
13+
#define PACKAGE_BUGREPORT "@LTFS_BUGREPORT@"
14+
#define PACKAGE_URL ""
15+
#define VERSION "@LTFS_VERSION_FULL@"
16+
17+
/* Referenced by the sources */
18+
#cmakedefine HAVE_SYS_SYSCTL_H 1
19+
#cmakedefine HAVE_XML_PARSE_HUGE 1
20+
#cmakedefine HAVE_XML_READER_SETUP 1
21+
#define SIZEOF_TIME_T @SIZEOF_TIME_T@
22+
23+
/* Header availability (parity with autotools; mostly unreferenced) */
24+
#cmakedefine HAVE_FCNTL_H 1
25+
#cmakedefine HAVE_LIMITS_H 1
26+
#cmakedefine HAVE_STDDEF_H 1
27+
#cmakedefine HAVE_STDINT_H 1
28+
#cmakedefine HAVE_STDLIB_H 1
29+
#cmakedefine HAVE_STRING_H 1
30+
#cmakedefine HAVE_SYS_IOCTL_H 1
31+
#cmakedefine HAVE_SYS_MOUNT_H 1
32+
#cmakedefine HAVE_SYS_TIME_H 1
33+
#cmakedefine HAVE_TERMIOS_H 1
34+
#cmakedefine HAVE_UNISTD_H 1
35+
36+
#endif /* LTFS_CMAKE_CONFIG_H */

0 commit comments

Comments
 (0)