Files
tyche/CMakeLists.txt
2026-05-13 07:19:33 -05:00

176 lines
6.0 KiB
CMake

cmake_minimum_required(VERSION 3.20)
project(tyche
VERSION 0.1.0
LANGUAGES C
)
# ---------------------------------------------------------------------------
# Project-wide settings
# ---------------------------------------------------------------------------
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
# Default to Release if not explicitly set (single-config generators only)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
endif()
# Toggle between STATIC and SHARED for the main library
option(BUILD_SHARED_LIB "Build mylib as a shared library" OFF)
include(GNUInstallDirs)
# ---------------------------------------------------------------------------
# Warning flags loaded from external files
# ---------------------------------------------------------------------------
# Each file contains one flag per line, e.g.:
# -Wall
# -Wextra
# -Wshadow
# Lines starting with `#` are treated as comments.
function(read_flags_file path out_var)
set(flags "")
if(EXISTS "${path}")
file(STRINGS "${path}" lines)
foreach(line IN LISTS lines)
string(STRIP "${line}" line)
if(line AND NOT line MATCHES "^#")
list(APPEND flags "${line}")
endif()
endforeach()
else()
message(WARNING "Warnings file not found: ${path}")
endif()
set(${out_var} "${flags}" PARENT_SCOPE)
endfunction()
set(WARNINGS_DIR "${CMAKE_SOURCE_DIR}/cmake/warnings")
read_flags_file("${WARNINGS_DIR}/common.txt" COMMON_WARNINGS)
read_flags_file("${WARNINGS_DIR}/gcc.txt" GCC_WARNINGS)
read_flags_file("${WARNINGS_DIR}/clang.txt" CLANG_WARNINGS)
# Build a single list of warnings appropriate for the current compiler
set(PROJECT_WARNINGS ${COMMON_WARNINGS})
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
list(APPEND PROJECT_WARNINGS ${GCC_WARNINGS})
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") # matches Clang and AppleClang
list(APPEND PROJECT_WARNINGS ${CLANG_WARNINGS})
endif()
# ---------------------------------------------------------------------------
# Per-config compile/link options exposed via an INTERFACE target
# ---------------------------------------------------------------------------
# Anything that links `project_options` inherits warnings + debug/release tweaks.
add_library(project_options INTERFACE)
# Warnings apply to every build type
target_compile_options(project_options INTERFACE ${PROJECT_WARNINGS})
# Debug-only flags
target_compile_options(project_options INTERFACE
"$<$<CONFIG:Debug>:-O0>"
"$<$<CONFIG:Debug>:-g3>"
"$<$<CONFIG:Debug>:-fno-omit-frame-pointer>"
"$<$<CONFIG:Debug>:-fsanitize=address>"
"$<$<CONFIG:Debug>:-fsanitize=undefined>"
)
target_link_options(project_options INTERFACE
"$<$<CONFIG:Debug>:-fsanitize=address>"
"$<$<CONFIG:Debug>:-fsanitize=undefined>"
)
# LeakSanitizer is not supported on macOS — gate it on platform.
# (On Linux it's already bundled into ASan, but adding the flag is harmless
# and makes intent explicit; on macOS it would fail to link.)
if(NOT APPLE)
target_compile_options(project_options INTERFACE
"$<$<CONFIG:Debug>:-fsanitize=leak>"
)
target_link_options(project_options INTERFACE
"$<$<CONFIG:Debug>:-fsanitize=leak>"
)
endif()
target_compile_definitions(project_options INTERFACE
"$<$<CONFIG:Debug>:DEBUG_BUILD=1>"
)
# Release-only flags
target_compile_options(project_options INTERFACE
"$<$<CONFIG:Release>:-O3>"
"$<$<CONFIG:Release>:-DNDEBUG>"
)
target_link_options(project_options INTERFACE
"$<$<CONFIG:Release>:-Wl,--as-needed>"
)
# ---------------------------------------------------------------------------
# The library
# ---------------------------------------------------------------------------
if(BUILD_SHARED_LIB)
set(MYLIB_KIND SHARED)
else()
set(MYLIB_KIND STATIC)
endif()
add_library(mylib ${MYLIB_KIND}
src/mylib/foo.c
src/mylib/bar.c
)
add_library(MyProject::mylib ALIAS mylib)
target_include_directories(mylib
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE
"${CMAKE_SOURCE_DIR}/src"
)
target_link_libraries(mylib PRIVATE project_options)
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
POSITION_INDEPENDENT_CODE ON
)
# ---------------------------------------------------------------------------
# Main executable
# ---------------------------------------------------------------------------
add_executable(myapp src/app/main.c)
target_link_libraries(myapp PRIVATE MyProject::mylib project_options)
# ---------------------------------------------------------------------------
# Test executable
# ---------------------------------------------------------------------------
enable_testing()
add_executable(mylib_tests tests/test_main.c)
target_link_libraries(mylib_tests PRIVATE MyProject::mylib project_options)
add_test(NAME mylib_tests COMMAND mylib_tests)
# ---------------------------------------------------------------------------
# Install rules (executable and library only — tests excluded)
# ---------------------------------------------------------------------------
install(TARGETS mylib myapp
EXPORT MyProjectTargets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h"
)
# Optional: export a CMake package config so downstream projects can do
# find_package(MyProject CONFIG REQUIRED)
install(EXPORT MyProjectTargets
FILE MyProjectTargets.cmake
NAMESPACE MyProject::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
)