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 "$<$:-O0>" "$<$:-g3>" "$<$:-fno-omit-frame-pointer>" "$<$:-fsanitize=address>" "$<$:-fsanitize=undefined>" ) target_link_options(project_options INTERFACE "$<$:-fsanitize=address>" "$<$:-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 "$<$:-fsanitize=leak>" ) target_link_options(project_options INTERFACE "$<$:-fsanitize=leak>" ) endif() target_compile_definitions(project_options INTERFACE "$<$:DEBUG_BUILD=1>" ) # Release-only flags target_compile_options(project_options INTERFACE "$<$:-O3>" "$<$:-DNDEBUG>" ) target_link_options(project_options INTERFACE "$<$:-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 "$" "$" 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 )