|
|
@@ -73,3 +73,105 @@ macro(createTraceLevelOptions)
|
|
|
|
|
|
add_definitions(-DOPENER_TRACE_LEVEL=${TRACE_LEVEL})
|
|
|
endmacro(createTraceLevelOptions)
|
|
|
+
|
|
|
+#######################################
|
|
|
+# Build hardening, sanitizers, coverage
|
|
|
+#######################################
|
|
|
+
|
|
|
+# Easy toggles for sanitizers, hardening, coverage and warnings
|
|
|
+option(OpENer_ENABLE_ASAN "Enable AddressSanitizer" OFF)
|
|
|
+option(OpENer_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
|
|
|
+option(OpENer_ENABLE_TSAN "Enable ThreadSanitizer" OFF)
|
|
|
+option(OpENer_ENABLE_MSAN "Enable MemorySanitizer (Clang only)" OFF)
|
|
|
+option(OpENer_ENABLE_LSAN "Enable LeakSanitizer" OFF)
|
|
|
+option(OpENer_ENABLE_COVERAGE "Enable code coverage flags" OFF)
|
|
|
+option(OpENer_ENABLE_HARDENING "Enable hardening flags (FORTIFY, PIE, RELRO, stack protector)" OFF)
|
|
|
+option(OpENer_ENABLE_STRICT_WARNINGS "Enable comprehensive warnings" OFF)
|
|
|
+
|
|
|
+function(opener_apply_build_options)
|
|
|
+ # Compose sanitizer list
|
|
|
+ set(_sanitizers "")
|
|
|
+ if(OpENer_ENABLE_ASAN)
|
|
|
+ list(APPEND _sanitizers address)
|
|
|
+ endif()
|
|
|
+ if(OpENer_ENABLE_UBSAN)
|
|
|
+ list(APPEND _sanitizers undefined)
|
|
|
+ endif()
|
|
|
+ if(OpENer_ENABLE_TSAN)
|
|
|
+ list(APPEND _sanitizers thread)
|
|
|
+ endif()
|
|
|
+ if(OpENer_ENABLE_MSAN)
|
|
|
+ list(APPEND _sanitizers memory)
|
|
|
+ endif()
|
|
|
+ if(OpENer_ENABLE_LSAN)
|
|
|
+ list(APPEND _sanitizers leak)
|
|
|
+ endif()
|
|
|
+
|
|
|
+ if(_sanitizers)
|
|
|
+ string(REPLACE ";" "," _sanitizer_string "${_sanitizers}")
|
|
|
+ # Apply to compile and link flags only for enabled languages
|
|
|
+ foreach(lang C CXX)
|
|
|
+ # Skip languages that haven't been enabled
|
|
|
+ if(NOT DEFINED CMAKE_${lang}_COMPILER_ID)
|
|
|
+ continue()
|
|
|
+ endif()
|
|
|
+ if(CMAKE_${lang}_COMPILER_ID MATCHES "Clang" OR CMAKE_${lang}_COMPILER_ID MATCHES "GNU")
|
|
|
+ set(var_cflags "${CMAKE_${lang}_FLAGS}")
|
|
|
+ set(var_cflags "${var_cflags} -fsanitize=${_sanitizer_string} -fno-omit-frame-pointer")
|
|
|
+ set(CMAKE_${lang}_FLAGS "${var_cflags}")
|
|
|
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${_sanitizer_string}")
|
|
|
+ else()
|
|
|
+ message(WARNING "Sanitizers requested but unsupported compiler: ${CMAKE_${lang}_COMPILER_ID}")
|
|
|
+ endif()
|
|
|
+ endforeach()
|
|
|
+ # UBSan: add recover behavior to aid debugging if only UBSan requested
|
|
|
+ if(OpENer_ENABLE_UBSAN)
|
|
|
+ add_compile_options(-fno-sanitize-recover=undefined)
|
|
|
+ endif()
|
|
|
+ endif()
|
|
|
+
|
|
|
+ if(OpENer_ENABLE_COVERAGE)
|
|
|
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "")
|
|
|
+ message(STATUS "Enabling code coverage flags")
|
|
|
+ foreach(lang C CXX)
|
|
|
+ set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -g -O0 --coverage")
|
|
|
+ endforeach()
|
|
|
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
|
|
|
+ else()
|
|
|
+ message(WARNING "Coverage requested but build type is not Debug; consider using -DCMAKE_BUILD_TYPE=Debug")
|
|
|
+ endif()
|
|
|
+ endif()
|
|
|
+
|
|
|
+ if(OpENer_ENABLE_HARDENING)
|
|
|
+ message(STATUS "Enabling hardening/fortify flags")
|
|
|
+ add_compile_options(-fstack-protector-strong -D_FORTIFY_SOURCE=2)
|
|
|
+ # PIE and RELRO
|
|
|
+ add_compile_options(-fPIE)
|
|
|
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now -pie")
|
|
|
+ endif()
|
|
|
+
|
|
|
+ if(OpENer_ENABLE_STRICT_WARNINGS)
|
|
|
+ message(STATUS "Enabling strict warning flags")
|
|
|
+ add_compile_options(-Wall -Wextra -Wpedantic -Wconversion -Wshadow -Wdouble-promotion -Wformat=2)
|
|
|
+ endif()
|
|
|
+endfunction(opener_apply_build_options)
|
|
|
+
|
|
|
+## If this is a Debug or CI/Test build, enable a sensible default set of
|
|
|
+## sanitizers, coverage, hardening and strict warnings to make CI runs
|
|
|
+## more effective. This will apply when the build type is Debug, tests are
|
|
|
+## enabled (OpENer_TESTS) or the CI environment variable is present. These
|
|
|
+## cache variables are forced here so CI scripts that don't pass flags still
|
|
|
+## get the helpful defaults.
|
|
|
+if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR OpENer_TESTS OR DEFINED ENV{CI})
|
|
|
+ message(STATUS "CI/Debug/Test detected: enabling sanitizer/coverage/hardening defaults")
|
|
|
+ set(OpENer_ENABLE_ASAN ON CACHE BOOL "Enable AddressSanitizer" FORCE)
|
|
|
+ set(OpENer_ENABLE_UBSAN ON CACHE BOOL "Enable UndefinedBehaviorSanitizer" FORCE)
|
|
|
+ set(OpENer_ENABLE_LSAN ON CACHE BOOL "Enable LeakSanitizer" FORCE)
|
|
|
+ set(OpENer_ENABLE_COVERAGE ON CACHE BOOL "Enable code coverage flags" FORCE)
|
|
|
+ set(OpENer_ENABLE_HARDENING ON CACHE BOOL "Enable hardening flags (FORTIFY, PIE, RELRO, stack protector)" FORCE)
|
|
|
+ set(OpENer_ENABLE_STRICT_WARNINGS ON CACHE BOOL "Enable comprehensive warnings" FORCE)
|
|
|
+ # MSAN and TSAN remain opt-in only due to toolchain/runtime requirements
|
|
|
+endif()
|
|
|
+
|
|
|
+# Apply options immediately so callers don't need to remember to invoke the function
|
|
|
+opener_apply_build_options()
|