Change cmakelists for compile strictness

This commit is contained in:
2026-08-16 20:48:24 +02:00
parent 309367a91e
commit 914fa6e5a7
+266 -4
View File
@@ -9,6 +9,242 @@ set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
# ---------------------------------------------------------
# Build configuration
#
# cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # -Og -g3, ASan+UBSan, GCC -fanalyzer
# cmake -S . -B build -DCMAKE_BUILD_TYPE=Release # -O3, _FORTIFY_SOURCE, no instrumentation
#
# Debug is tuned to *find* memory bugs, Release to *survive* them. The warning
# set is the same in both; only the instrumentation differs.
# ---------------------------------------------------------
get_property(SKALACOIN_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT SKALACOIN_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
message(STATUS "No CMAKE_BUILD_TYPE specified; defaulting to Debug")
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
# OFF until src/ is clean under the warning set below; flip it on afterwards so
# it stays clean.
option(SKALACOIN_WERROR "Debug: treat warnings as errors" OFF)
option(SKALACOIN_ENABLE_SANITIZERS "Debug: build with AddressSanitizer + UndefinedBehaviorSanitizer" ON)
option(SKALACOIN_ENABLE_ANALYZER "Debug: run the compiler's static analyzer (GCC -fanalyzer)" ON)
option(SKALACOIN_ENABLE_HARDENING "All configs: stack protector, _FORTIFY_SOURCE, RELRO/NOW, CFI" ON)
option(SKALACOIN_ENABLE_LTO "Optimized configs: link-time optimization" OFF)
include(CheckCCompilerFlag)
include(CheckLinkerFlag)
# Most of the interesting memory diagnostics below only exist from a certain
# GCC/Clang version onwards, and several are architecture specific. Probe every
# flag instead of gating on compiler version, so an older or foreign toolchain
# silently gets the subset it understands rather than failing to configure.
#
# A flag the driver merely tolerates is not a flag that does anything (Clang
# accepts -fstack-clash-protection on arm64 and then ignores it), so the probe
# promotes "argument unused" to an error where the compiler supports that.
check_c_compiler_flag(-Werror=unused-command-line-argument SKALACOIN_HAS_WERROR_UNUSED_ARG)
if(SKALACOIN_HAS_WERROR_UNUSED_ARG)
set(SKALACOIN_FLAG_PROBE_STRICT "-Werror=unused-command-line-argument")
else()
set(SKALACOIN_FLAG_PROBE_STRICT "")
endif()
function(skalacoin_append_supported_c_flags out_var)
set(_accepted ${${out_var}})
set(CMAKE_REQUIRED_FLAGS "${SKALACOIN_FLAG_PROBE_STRICT}")
foreach(_flag IN LISTS ARGN)
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_CFLAG_${_flag}" _cache_var)
check_c_compiler_flag("${_flag}" ${_cache_var})
if(${_cache_var})
list(APPEND _accepted "${_flag}")
endif()
endforeach()
set(${out_var} "${_accepted}" PARENT_SCOPE)
endfunction()
# Instrumentation such as -fsanitize=address has to reach the linker as well,
# and is only usable if the matching runtime library actually exists, so the
# probe hands the flag to both halves of the try-compile.
function(skalacoin_append_supported_instrument_flags out_var)
set(_accepted ${${out_var}})
foreach(_flag IN LISTS ARGN)
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_INSTRUMENT_${_flag}" _cache_var)
set(CMAKE_REQUIRED_FLAGS "${SKALACOIN_FLAG_PROBE_STRICT}")
set(CMAKE_REQUIRED_LINK_OPTIONS "${_flag}")
check_c_compiler_flag("${_flag}" ${_cache_var})
if(${_cache_var})
list(APPEND _accepted "${_flag}")
endif()
endforeach()
set(${out_var} "${_accepted}" PARENT_SCOPE)
endfunction()
function(skalacoin_append_supported_link_flags out_var)
set(_accepted ${${out_var}})
foreach(_flag IN LISTS ARGN)
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_LDFLAG_${_flag}" _cache_var)
check_linker_flag(C "${_flag}" ${_cache_var})
if(${_cache_var})
list(APPEND _accepted "${_flag}")
endif()
endforeach()
set(${out_var} "${_accepted}" PARENT_SCOPE)
endfunction()
# Warnings for our own C code; the per-config flag sets are applied through
# generator expressions so multi-config generators (Xcode, VS) work too.
set(SKALACOIN_C_WARNINGS "")
set(SKALACOIN_C_FLAGS_DEBUG "")
set(SKALACOIN_C_FLAGS_OPTIMIZED "")
# Instrumentation that must be handed to the linker as well, and that also
# covers the vendored code we link in.
set(SKALACOIN_INSTRUMENT_COMPILE "")
set(SKALACOIN_INSTRUMENT_LINK "")
if(MSVC)
list(APPEND SKALACOIN_C_WARNINGS /W4 /permissive- /sdl)
list(APPEND SKALACOIN_C_FLAGS_DEBUG /Od /RTC1 /GS)
list(APPEND SKALACOIN_C_FLAGS_OPTIMIZED /O2 /GS /guard:cf)
if(SKALACOIN_WERROR)
list(APPEND SKALACOIN_C_FLAGS_DEBUG /WX)
endif()
if(SKALACOIN_ENABLE_ANALYZER)
list(APPEND SKALACOIN_C_FLAGS_DEBUG /analyze)
endif()
if(SKALACOIN_ENABLE_SANITIZERS)
list(APPEND SKALACOIN_INSTRUMENT_COMPILE /fsanitize=address)
endif()
else()
# Portable warning set, memory-safety first.
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
-Wall
-Wextra
-Wpedantic
-Wshadow
-Wconversion
-Wsign-conversion
-Wcast-qual
-Wcast-align
-Wstrict-prototypes
-Wmissing-prototypes
-Wold-style-definition
-Wbad-function-cast
-Wwrite-strings
-Wformat=2
-Wnull-dereference
-Wdouble-promotion
-Wfloat-equal # consensus code must stay integer-only
-Wvla
-Wstack-protector
-Wpointer-arith
-Wundef
-Winit-self
-Wmissing-include-dirs
)
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
# GCC-only diagnostics. The -W*=N forms ask for the strictest level:
# more false positives, but they catch out-of-bounds writes, dangling
# pointers and double frees that -Wall/-Wextra walk straight past.
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
-Warray-bounds=2
-Wstringop-overflow=4
-Wstringop-truncation
-Wformat-overflow=2
-Wformat-truncation=2
-Wuse-after-free=3
-Wdangling-pointer=2
-Wfree-nonheap-object
-Walloc-zero
-Walloca
-Wduplicated-cond
-Wduplicated-branches
-Wlogical-op
-Wjump-misses-init
-Wtrampolines
-Wflex-array-member-not-at-end
)
# -fanalyzer is a whole-path symbolic execution pass (leaks, double
# free, use-after-free, NULL derefs across function boundaries). It is
# slow, so Debug only.
if(SKALACOIN_ENABLE_ANALYZER)
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_DEBUG -fanalyzer)
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
# Clang-only diagnostics. Clang has no in-compiler equivalent of
# -fanalyzer; run `scan-build cmake --build build` for that.
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
-Warray-bounds-pointer-arithmetic
-Wconditional-uninitialized
-Wshift-sign-overflow
-Wassign-enum
-Wcomma
-Wloop-analysis
-Wthread-safety
-Wover-aligned
)
endif()
# -Og keeps the code steppable while still running the optimizer passes
# that -Wmaybe-uninitialized / -Wstringop-* rely on; at -O0 those warnings
# go quiet. -fno-omit-frame-pointer buys readable sanitizer backtraces.
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_DEBUG
-Og
-g3
-fno-omit-frame-pointer
)
if(SKALACOIN_WERROR)
list(APPEND SKALACOIN_C_FLAGS_DEBUG -Werror)
endif()
if(SKALACOIN_ENABLE_HARDENING)
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_DEBUG
-fstack-protector-strong
)
# _FORTIFY_SOURCE needs an optimized build to see through the buffer
# sizes, and it fights ASan's interceptors, so it is Release-only.
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_OPTIMIZED
-U_FORTIFY_SOURCE
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-fstack-clash-protection
)
skalacoin_append_supported_instrument_flags(SKALACOIN_INSTRUMENT_COMPILE
-fcf-protection=full # x86_64 CET
-mbranch-protection=standard # aarch64 BTI/PAC
)
skalacoin_append_supported_link_flags(SKALACOIN_INSTRUMENT_LINK
"LINKER:-z,relro"
"LINKER:-z,now"
"LINKER:-z,noexecstack"
)
endif()
if(SKALACOIN_ENABLE_SANITIZERS)
# -fno-sanitize-recover makes UB abort instead of printing and
# continuing, so a bad shift or overflow cannot be ignored in CI.
set(SKALACOIN_SANITIZER_FLAGS "")
skalacoin_append_supported_instrument_flags(SKALACOIN_SANITIZER_FLAGS
-fsanitize=address
-fsanitize=undefined
-fno-sanitize-recover=undefined
-fno-omit-frame-pointer
)
list(APPEND SKALACOIN_INSTRUMENT_COMPILE "$<$<CONFIG:Debug>:${SKALACOIN_SANITIZER_FLAGS}>")
list(APPEND SKALACOIN_INSTRUMENT_LINK "$<$<CONFIG:Debug>:${SKALACOIN_SANITIZER_FLAGS}>")
endif()
endif()
if(SKALACOIN_ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT SKALACOIN_IPO_SUPPORTED OUTPUT SKALACOIN_IPO_ERROR)
if(NOT SKALACOIN_IPO_SUPPORTED)
message(WARNING "LTO requested but unsupported by this toolchain: ${SKALACOIN_IPO_ERROR}")
endif()
endif()
find_package(Threads REQUIRED)
include(FetchContent)
@@ -146,6 +382,11 @@ if(SKALACOIN_ENABLE_AUTOLYKOS2_REF)
add_library(autolykos2_ref STATIC ${AUTOLYKOS2_REF_SOURCES})
target_include_directories(autolykos2_ref PRIVATE ${AUTOLYKOS2_REF_BASE}/include)
# Vendored code gets the instrumentation but not our warning set: sanitizers
# only see a bug if the translation unit that owns the memory is compiled
# with them, and this library allocates buffers that our code touches.
target_compile_options(autolykos2_ref PRIVATE "${SKALACOIN_INSTRUMENT_COMPILE}")
target_link_options(autolykos2_ref PRIVATE "${SKALACOIN_INSTRUMENT_LINK}")
# Upstream source uses `malloc/free/exit/EXIT_FAILURE` without including
# stdlib headers in some C++ translation units. AppleClang can compile this,
# while Linux Clang fails. Force-include stdlib.h for C++ in this vendored lib.
@@ -221,11 +462,21 @@ target_include_directories(node PRIVATE
${PROJECT_SOURCE_DIR}/include
)
target_compile_options(node PRIVATE
-Wall
-Wextra
-Wpedantic
-g
"${SKALACOIN_C_WARNINGS}"
"${SKALACOIN_INSTRUMENT_COMPILE}"
"$<$<CONFIG:Debug>:${SKALACOIN_C_FLAGS_DEBUG}>"
"$<$<NOT:$<CONFIG:Debug>>:${SKALACOIN_C_FLAGS_OPTIMIZED}>"
)
target_link_options(node PRIVATE "${SKALACOIN_INSTRUMENT_LINK}")
if(SKALACOIN_ENABLE_LTO AND SKALACOIN_IPO_SUPPORTED)
set_target_properties(node PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE ON
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON
INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON
)
endif()
target_compile_definitions(node PRIVATE
CHAIN_DATA_DIR="${CMAKE_BINARY_DIR}/chain_data"
$<$<BOOL:${SKALACOIN_AUTOLYKOS2_REF_AVAILABLE}>:SKALACOIN_AUTOLYKOS2_REF_AVAILABLE>
@@ -235,3 +486,14 @@ target_compile_definitions(node PRIVATE
$<$<BOOL:1>:_DEFAULT_SOURCE>
)
set_target_properties(node PROPERTIES OUTPUT_NAME "skalacoin_node")
# ---------------------------------------------------------
# Configuration summary
# ---------------------------------------------------------
message(STATUS "skalacoin: build type ${CMAKE_BUILD_TYPE}")
message(STATUS "skalacoin: compiler ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
message(STATUS "skalacoin: warnings-as-err ${SKALACOIN_WERROR}")
message(STATUS "skalacoin: sanitizers ${SKALACOIN_ENABLE_SANITIZERS} (Debug only) [${SKALACOIN_SANITIZER_FLAGS}]")
message(STATUS "skalacoin: static analyzer ${SKALACOIN_ENABLE_ANALYZER} (Debug only, GCC)")
message(STATUS "skalacoin: hardening ${SKALACOIN_ENABLE_HARDENING}")
message(STATUS "skalacoin: LTO ${SKALACOIN_ENABLE_LTO}")