diff --git a/CMakeLists.txt b/CMakeLists.txt index bad8ad1..03891fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,18 @@ project( VERSION 1.0 LANGUAGES C) +# ------------------------------------------------ +# Estandard C +# ------------------------------------------------ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) -# clangd +# Exportar compile_commands.json (clangd / IDEs) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # ------------------------------------------------ -# Detectar si este proyecto es el principal +# Is Top project? # ------------------------------------------------ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(ARRAYLIST_IS_TOP_LEVEL ON) @@ -19,32 +23,76 @@ else() set(ARRAYLIST_IS_TOP_LEVEL OFF) endif() +# ------------------------------------------------ # Opciones -option(ARRAYLIST_BUILD_TESTS "Build arraylist tests" ${ARRAYLIST_IS_TOP_LEVEL}) -option(ARRAYLIST_ENABLE_SANITIZERS "Enable sanitizers for tests" ON) +# ------------------------------------------------ +option(ARRAYLIST_BUILD_TESTS "Build tests" ${ARRAYLIST_IS_TOP_LEVEL}) +option(ARRAYLIST_BUILD_EXAMPLE "Build example" ${ARRAYLIST_IS_TOP_LEVEL}) +option(ARRAYLIST_ENABLE_INSTALL "Enable install" ${ARRAYLIST_IS_TOP_LEVEL}) +option(ARRAYLIST_ENABLE_SANITIZERS "Enable Sanitizers for test" ON) -# ------------------------ -# Library -# ------------------------ +# ------------------------------------------------ +# Warning Flags (GCC/Clang vs MSVC) +# ------------------------------------------------ +if(MSVC) + set(ARRAYLIST_WARNING_FLAGS /W4) +else() + set(ARRAYLIST_WARNING_FLAGS -Wall -Wextra -Wpedantic) +endif() + +# ------------------------------------------------ +# Main Library +# ------------------------------------------------ add_library(arraylist src/arraylist.c) +# Alias con namespace → los consumidores via CPM/FetchContent pueden escribir +# target_link_libraries(mi_app arraylist::arraylist) +add_library(arraylist::arraylist ALIAS arraylist) + target_include_directories( arraylist PUBLIC $ $) -target_compile_options(arraylist PRIVATE -Wall -Wextra -Wpedantic) +# Flagas only for compile lib +target_compile_options(arraylist PRIVATE ${ARRAYLIST_WARNING_FLAGS}) -# ------------------------ -# Example (opcional) -# ------------------------ -add_executable(arraylist_example src/main.c) +# Propagates c11 as requirement +target_compile_features(arraylist PUBLIC c_std_11) -target_link_libraries(arraylist_example arraylist) +# ------------------------------------------------ +# Example only built when is main proyect +# ------------------------------------------------ +if(ARRAYLIST_BUILD_EXAMPLE) + add_executable(arraylist_example src/main.c) + target_link_libraries(arraylist_example PRIVATE arraylist) +endif() -# ------------------------ -# Testing -# ------------------------ +# ------------------------------------------------ +# Tests +# ------------------------------------------------ if(ARRAYLIST_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() + +# ------------------------------------------------ +# Installation rules +# ------------------------------------------------ +if(ARRAYLIST_ENABLE_INSTALL) + include(GNUInstallDirs) + + install( + TARGETS arraylist + EXPORT arraylistTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + + install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + install( + EXPORT arraylistTargets + FILE arraylistTargets.cmake + NAMESPACE arraylist:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/arraylist) +endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4145dc2..99e14c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,23 +1,121 @@ -find_package(cmocka REQUIRED) +# --------------------------------------------------------------- +# First search cmocka and automatically download if necessary +# --------------------------------------------------------------- +find_package(cmocka QUIET) -add_executable(test_arraylist test_arraylist.c) +if(NOT cmocka_FOUND) + message( + STATUS + "[arraylist] cmocka was not fount in the system — downloading with FetchContent..." + ) -target_link_libraries(test_arraylist - arraylist - cmocka::cmocka -) + include(FetchContent) + FetchContent_Declare( + cmocka + GIT_REPOSITORY https://git.cryptomilk.org/projects/cmocka.git + GIT_TAG cmocka-1.1.7 + GIT_SHALLOW TRUE) -# Sanitizers (portables) -if(ARRAYLIST_ENABLE_SANITIZERS) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(test_arraylist PRIVATE - -fsanitize=address - -fno-omit-frame-pointer - ) - target_link_options(test_arraylist PRIVATE - -fsanitize=address - ) + set(WITH_STATIC_LIB + ON + CACHE BOOL "" FORCE) + set(WITH_SHARED_LIB + OFF + CACHE BOOL "" FORCE) + set(WITH_CMOCKERY_SUPPORT + OFF + CACHE BOOL "" FORCE) + set(WITH_EXAMPLES + OFF + CACHE BOOL "" FORCE) + set(UNIT_TESTING + OFF + CACHE BOOL "" FORCE) + set(PICKY_DEVELOPER + OFF + CACHE BOOL "" FORCE) + + FetchContent_MakeAvailable(cmocka) + + # cmocka can export its target as "cmocka" or "cmocka-static" create alias if + # it doesnt exist + if(NOT TARGET cmocka::cmocka) + if(TARGET cmocka-static) + add_library(cmocka::cmocka ALIAS cmocka-static) + elseif(TARGET cmocka) + add_library(cmocka::cmocka ALIAS cmocka) + else() + message( + WARNING + "[arraylist] cmocka::cmocka target could not be tested — skipping tests" + ) + return() endif() + endif() endif() +# --------------------------------------------------------------- +# Verifiy ASAN support +# --------------------------------------------------------------- +set(ARRAYLIST_USE_ASAN OFF) + +if(ARRAYLIST_ENABLE_SANITIZERS) + if(MSVC) + if(MSVC_VERSION GREATER_EQUAL 1928) + set(ARRAYLIST_USE_ASAN ON) + else() + message( + STATUS + "[arraylist] MSVC < 16.9 doesnt support ASAN — sanitizers deactivated." + ) + endif() + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT WIN32) + include(CheckCCompilerFlag) + check_c_compiler_flag(-fsanitize=address ARRAYLIST_COMPILER_HAS_ASAN) + if(ARRAYLIST_COMPILER_HAS_ASAN) + set(ARRAYLIST_USE_ASAN ON) + else() + message( + STATUS + "[arraylist] Compiler doesnt support -fsanitize=address — sanitizers deactivated." + ) + endif() + else() + message( + STATUS + "[arraylist] Platform/compiler doesnt support ASAN — sanitizers deactivated." + ) + endif() + + if(ARRAYLIST_USE_ASAN) + message(STATUS "[arraylist] ASAN Enabled for tests") + endif() +endif() + +# --------------------------------------------------------------- +# Test execuatable +# --------------------------------------------------------------- +add_executable(test_arraylist test_arraylist.c) + +target_link_libraries(test_arraylist PRIVATE arraylist cmocka::cmocka) + +# Same as lib +target_compile_features(test_arraylist PRIVATE c_std_11) + +# Apply asan +if(ARRAYLIST_USE_ASAN) + if(MSVC) + target_compile_options(test_arraylist PRIVATE /fsanitize=address) + else() + target_compile_options(test_arraylist PRIVATE -fsanitize=address + -fno-omit-frame-pointer) + target_link_options(test_arraylist PRIVATE -fsanitize=address) + endif() +endif() + +# --------------------------------------------------------------- +# Register with CTests +# --------------------------------------------------------------- add_test(NAME arraylist_tests COMMAND test_arraylist) + +set_tests_properties(arraylist_tests PROPERTIES TIMEOUT 30)