314 lines
11 KiB
CMake
314 lines
11 KiB
CMake
|
|
cmake_minimum_required(VERSION 3.10)
|
|
project(webserv)
|
|
|
|
set(CMAKE_CXX_STANDARD 20)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
|
|
# Enable testing
|
|
enable_testing()
|
|
|
|
# Enable parallel compilation
|
|
include(ProcessorCount)
|
|
ProcessorCount(N)
|
|
if(NOT N EQUAL 0)
|
|
set(CMAKE_BUILD_PARALLEL_LEVEL ${N})
|
|
endif()
|
|
|
|
# Add source files
|
|
file(GLOB_RECURSE SOURCES
|
|
"${PROJECT_SOURCE_DIR}/webserv/*.cpp"
|
|
)
|
|
|
|
# Remove main.cpp from sources for library (we'll add it back for the main executable)
|
|
list(FILTER SOURCES EXCLUDE REGEX ".*main\\.cpp$")
|
|
|
|
# Add include directories
|
|
include_directories(
|
|
${PROJECT_SOURCE_DIR}
|
|
)
|
|
|
|
|
|
# Build type options
|
|
option(ENABLE_COVERAGE "Enable code coverage" OFF)
|
|
|
|
if(NOT CMAKE_BUILD_TYPE)
|
|
set(CMAKE_BUILD_TYPE Release)
|
|
endif()
|
|
|
|
# Define available build types for IDE
|
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
|
"Debug"
|
|
"Release"
|
|
"ASAN"
|
|
"RelWithDebInfo"
|
|
"MinSizeRel"
|
|
)
|
|
|
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
|
|
|
# Coverage configuration
|
|
if(ENABLE_COVERAGE)
|
|
message(STATUS "Code coverage enabled")
|
|
|
|
# Find required tools
|
|
find_program(GCOV_PATH gcov)
|
|
find_program(LCOV_PATH lcov)
|
|
find_program(GENHTML_PATH genhtml)
|
|
find_program(GCOVR_PATH gcovr)
|
|
|
|
if(NOT GCOV_PATH)
|
|
message(FATAL_ERROR "gcov not found! Please install gcov. On Ubuntu: sudo apt-get install gcc")
|
|
endif()
|
|
|
|
# Check for coverage tools (prefer lcov, fallback to gcovr)
|
|
if(LCOV_PATH AND GENHTML_PATH)
|
|
message(STATUS "Found lcov and genhtml - using lcov for coverage reports")
|
|
set(COVERAGE_TOOL "lcov")
|
|
elseif(GCOVR_PATH)
|
|
message(STATUS "Found gcovr - using gcovr for coverage reports")
|
|
set(COVERAGE_TOOL "gcovr")
|
|
else()
|
|
message(STATUS "Coverage tools not found. Install with:")
|
|
message(STATUS " Ubuntu: sudo apt-get install lcov")
|
|
message(STATUS " Or: pip install gcovr")
|
|
message(STATUS "Coverage data will still be generated, but no HTML report")
|
|
set(COVERAGE_TOOL "none")
|
|
endif()
|
|
|
|
# Set coverage flags based on compiler
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
message(STATUS "Using GCC coverage flags")
|
|
set(COVERAGE_FLAGS "--coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline")
|
|
set(COVERAGE_LINK_FLAGS "--coverage")
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
message(STATUS "Using Clang coverage flags")
|
|
# Use --coverage for Clang to generate gcov-compatible data
|
|
set(COVERAGE_FLAGS "--coverage -fprofile-arcs -ftest-coverage")
|
|
set(COVERAGE_LINK_FLAGS "--coverage")
|
|
else()
|
|
message(FATAL_ERROR "Code coverage only supported with GCC or Clang")
|
|
endif()
|
|
|
|
# Apply coverage flags
|
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS}")
|
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}")
|
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}")
|
|
|
|
# Ensure debug information is available
|
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0")
|
|
endif()
|
|
|
|
# Set build flags for different build types
|
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
message(STATUS "Debug build: adding debug flags")
|
|
add_compile_options(-g -O0)
|
|
add_definitions(-DDEBUG)
|
|
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
message(STATUS "Release build: adding optimization flags")
|
|
# -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2
|
|
add_compile_options(-O2 -g -Wall -Wextra -Werror)
|
|
add_definitions(-DNDEBUG)
|
|
elseif(CMAKE_BUILD_TYPE STREQUAL "ASAN")
|
|
message(STATUS "AddressSanitizer build: adding sanitizer flags")
|
|
add_compile_options(-g -O1 -fsanitize=address -fno-omit-frame-pointer)
|
|
add_link_options(-fsanitize=address)
|
|
add_definitions(-DASAN)
|
|
endif()
|
|
|
|
# Add executable target
|
|
add_executable(webserv ${SOURCES} "${PROJECT_SOURCE_DIR}/webserv/main.cpp")
|
|
|
|
# Create a library for testing (without main.cpp)
|
|
add_library(webserv_lib ${SOURCES})
|
|
|
|
# Google Test integration
|
|
option(BUILD_TESTS "Build tests" ON)
|
|
|
|
if(BUILD_TESTS)
|
|
find_package(PkgConfig QUIET)
|
|
if(PkgConfig_FOUND)
|
|
pkg_check_modules(GTEST gtest)
|
|
pkg_check_modules(GTEST_MAIN gtest_main)
|
|
endif()
|
|
|
|
if(GTEST_FOUND AND GTEST_MAIN_FOUND)
|
|
message(STATUS "Using system Google Test")
|
|
# Use system gtest - variables will be set by pkg_check_modules
|
|
else()
|
|
message(STATUS "Downloading Google Test")
|
|
include(FetchContent)
|
|
FetchContent_Declare(
|
|
googletest
|
|
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
|
|
)
|
|
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(googletest)
|
|
|
|
# Make sure gtest targets are available
|
|
if(TARGET gtest_main)
|
|
message(STATUS "Google Test targets available")
|
|
endif()
|
|
endif()
|
|
|
|
# Add test directory if it exists
|
|
if(EXISTS "${CMAKE_SOURCE_DIR}/tests")
|
|
add_subdirectory(tests)
|
|
else()
|
|
message(STATUS "Tests directory not found, creating basic test structure")
|
|
file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/tests")
|
|
|
|
# Create a basic test file
|
|
file(WRITE "${CMAKE_SOURCE_DIR}/tests/test_main.cpp"
|
|
"#include <gtest/gtest.h>
|
|
|
|
// Basic test to verify Google Test is working
|
|
TEST(BasicTest, TruthTest) {
|
|
EXPECT_TRUE(true);
|
|
EXPECT_FALSE(false);
|
|
EXPECT_EQ(1 + 1, 2);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|
|
")
|
|
|
|
# Create CMakeLists.txt for tests
|
|
file(WRITE "${CMAKE_SOURCE_DIR}/tests/CMakeLists.txt"
|
|
"# Test executable
|
|
add_executable(webserv_tests
|
|
test_main.cpp
|
|
)
|
|
|
|
# Link with Google Test
|
|
if(TARGET gtest_main)
|
|
target_link_libraries(webserv_tests
|
|
gtest_main
|
|
webserv_lib
|
|
)
|
|
else()
|
|
target_link_libraries(webserv_tests
|
|
\${GTEST_LIBRARIES}
|
|
\${GTEST_MAIN_LIBRARIES}
|
|
webserv_lib
|
|
)
|
|
target_include_directories(webserv_tests PRIVATE \${GTEST_INCLUDE_DIRS})
|
|
endif()
|
|
|
|
target_include_directories(webserv_tests PRIVATE
|
|
\${CMAKE_SOURCE_DIR}
|
|
\${CMAKE_SOURCE_DIR}/webserv
|
|
)
|
|
|
|
# Discover tests
|
|
include(GoogleTest)
|
|
gtest_discover_tests(webserv_tests)
|
|
")
|
|
endif()
|
|
endif()
|
|
|
|
# Coverage targets
|
|
if(ENABLE_COVERAGE AND BUILD_TESTS)
|
|
# Coverage cleanup target
|
|
add_custom_target(coverage_clean
|
|
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage_report
|
|
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/coverage.info
|
|
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/coverage.xml
|
|
COMMENT "Cleaning coverage data"
|
|
)
|
|
|
|
if(COVERAGE_TOOL STREQUAL "lcov")
|
|
# Main coverage target using lcov
|
|
add_custom_target(coverage
|
|
DEPENDS webserv_tests
|
|
|
|
# Cleanup previous coverage data
|
|
COMMAND ${LCOV_PATH} --directory . --zerocounters
|
|
|
|
# Run tests
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
|
|
|
# Capture coverage data
|
|
COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
|
|
|
|
# Remove system and test files from coverage
|
|
COMMAND ${LCOV_PATH} --remove coverage.info '/usr/*' '/opt/*' '*/tests/*' '*/test/*' '*/_deps/*' '*/build/*' --output-file coverage.info
|
|
|
|
# Generate HTML report
|
|
COMMAND ${GENHTML_PATH} coverage.info --output-directory coverage_report --title "Webserv Coverage Report" --show-details --legend
|
|
|
|
# Print summary
|
|
COMMAND ${LCOV_PATH} --summary coverage.info
|
|
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating code coverage report with lcov"
|
|
)
|
|
|
|
# Coverage summary target
|
|
add_custom_target(coverage_summary
|
|
DEPENDS webserv_tests
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
|
COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
|
|
COMMAND ${LCOV_PATH} --remove coverage.info '/usr/*' '/opt/*' '*/tests/*' '*/test/*' '*/_deps/*' '*/build/*' --output-file coverage.info
|
|
COMMAND ${LCOV_PATH} --summary coverage.info
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating coverage summary"
|
|
)
|
|
|
|
elseif(COVERAGE_TOOL STREQUAL "gcovr")
|
|
# Coverage target using gcovr
|
|
add_custom_target(coverage
|
|
DEPENDS webserv_tests
|
|
|
|
# Run tests
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
|
|
|
# Generate HTML report
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*' --html --html-details -o coverage_report.html
|
|
|
|
# Generate XML report (for CI)
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*' --xml -o coverage.xml
|
|
|
|
# Print summary to console
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*'
|
|
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating code coverage report with gcovr"
|
|
)
|
|
|
|
# Coverage summary target
|
|
add_custom_target(coverage_summary
|
|
DEPENDS webserv_tests
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*'
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating coverage summary"
|
|
)
|
|
|
|
else()
|
|
# Basic coverage target without report generation
|
|
add_custom_target(coverage
|
|
DEPENDS webserv_tests
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
|
COMMAND ${CMAKE_COMMAND} -E echo "Coverage data generated. Install lcov or gcovr to generate HTML reports."
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Running tests with coverage enabled"
|
|
)
|
|
endif()
|
|
|
|
# Add coverage dependencies
|
|
if(TARGET coverage)
|
|
add_dependencies(coverage webserv_tests)
|
|
endif()
|
|
|
|
message(STATUS "Coverage targets available:")
|
|
message(STATUS " make coverage - Run tests and generate coverage report")
|
|
message(STATUS " make coverage_summary - Run tests and show coverage summary")
|
|
message(STATUS " make coverage_clean - Clean coverage data")
|
|
endif()
|
|
|