From b1f1ff6e1c0cdecfb4e56bb876ee5a58d7135f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Makrnac=C4=B1?= Date: Fri, 21 Nov 2025 03:13:19 +0300 Subject: [PATCH] Update module.cmake --- cpp/cmake/module.cmake | 298 +++++++++++++++++------------------------ 1 file changed, 125 insertions(+), 173 deletions(-) diff --git a/cpp/cmake/module.cmake b/cpp/cmake/module.cmake index f654774b..ef7a2e7a 100644 --- a/cpp/cmake/module.cmake +++ b/cpp/cmake/module.cmake @@ -1,98 +1,73 @@ -# copyright 2019 Spilsbury Holdings -# -# usage: barretenberg_module(module_name [dependencies ...]) -# -# Scans for all .cpp files in a subdirectory, and creates a library named . -# Scans for all .test.cpp files in a subdirectory, and creates a gtest binary named _tests. -# Scans for all .bench.cpp files in a subdirectory, and creates a benchmark binary named _bench. -# -# We have to get a bit complicated here, due to the fact CMake will not parallelise the building of object files -# between dependent targets, due to the potential of post-build code generation steps etc. -# To work around this, we create "object libraries" containing the object files. -# Then we declare executables/libraries that are to be built from these object files. -# These assets will only be linked as their dependencies complete, but we can parallelise the compilation at least. - function(barretenberg_module MODULE_NAME) - file(GLOB_RECURSE SOURCE_FILES *.cpp) - file(GLOB_RECURSE HEADER_FILES *.hpp *.tcc) + # 🛡️ SCOPE SAFETY: Initialize lists locally to avoid polluting parent scope accidentally + set(local_lib_targets "") + set(local_exe_targets "") + + # -------------------------------------------------------------------------- + # 1. SOURCE SCANNING + # -------------------------------------------------------------------------- + # 🚀 CRITICAL FIX: Added CONFIGURE_DEPENDS. + # Now CMake automatically re-runs if you add/remove files. No more ghost errors! + file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp) + file(GLOB_RECURSE HEADER_FILES CONFIGURE_DEPENDS *.hpp *.tcc) + + # Filter out tests, benchmarks, and fuzzers from the main library list(FILTER SOURCE_FILES EXCLUDE REGEX ".*\.(fuzzer|test|bench).cpp$") + # -------------------------------------------------------------------------- + # 2. MAIN LIBRARY BUILD + # -------------------------------------------------------------------------- if(SOURCE_FILES) - add_library( - ${MODULE_NAME}_objects - OBJECT - ${SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_objects) - - add_library( - ${MODULE_NAME} - STATIC - $ - ) - - target_link_libraries( - ${MODULE_NAME} - PUBLIC - ${ARGN} - ${TBB_IMPORTED_TARGETS} - ) - list(APPEND lib_targets ${MODULE_NAME}) - + # 🧠 OPTIMIZATION: Object Libraries allow parallel compilation of source files + # independent of the final link step. + add_library(${MODULE_NAME}_objects OBJECT ${SOURCE_FILES}) + list(APPEND local_lib_targets ${MODULE_NAME}_objects) + + # The actual static library consumes the objects + add_library(${MODULE_NAME} STATIC $) + + # Propagate dependencies to both the object lib (for compilation flags) + # and the static lib (for linking consumers) + target_link_libraries(${MODULE_NAME} PUBLIC ${ARGN} ${TBB_IMPORTED_TARGETS}) + + # Note: For Object libs, we often need to link deps specifically if they provide defines/includes + target_link_libraries(${MODULE_NAME}_objects PUBLIC ${ARGN} ${TBB_IMPORTED_TARGETS}) + + list(APPEND local_lib_targets ${MODULE_NAME}) + set(MODULE_LINK_NAME ${MODULE_NAME}) + else() + # If no source files, create an interface library so linking doesn't fail + add_library(${MODULE_NAME} INTERFACE) set(MODULE_LINK_NAME ${MODULE_NAME}) endif() - file(GLOB_RECURSE TEST_SOURCE_FILES *.test.cpp) + # -------------------------------------------------------------------------- + # 3. UNIT TESTS + # -------------------------------------------------------------------------- + file(GLOB_RECURSE TEST_SOURCE_FILES CONFIGURE_DEPENDS *.test.cpp) + if(TESTING AND TEST_SOURCE_FILES) - add_library( - ${MODULE_NAME}_test_objects - OBJECT - ${TEST_SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_test_objects) - - target_link_libraries( - ${MODULE_NAME}_test_objects - PRIVATE - GTest::gtest - env - ${TBB_IMPORTED_TARGETS} - ) - - add_executable( - ${MODULE_NAME}_tests - $ - ) - list(APPEND exe_targets ${MODULE_NAME}_tests) - - if(WASM) - target_link_options( - ${MODULE_NAME}_tests - PRIVATE - -Wl,-z,stack-size=8388608 - ) - endif() + add_library(${MODULE_NAME}_test_objects OBJECT ${TEST_SOURCE_FILES}) + list(APPEND local_lib_targets ${MODULE_NAME}_test_objects) + # Link dependencies to test objects for compilation headers + target_link_libraries(${MODULE_NAME}_test_objects PRIVATE GTest::gtest env ${TBB_IMPORTED_TARGETS}) + + # Conditional compilation definitions if(CI) - target_compile_definitions( - ${MODULE_NAME}_test_objects - PRIVATE - -DCI=1 - ) + target_compile_definitions(${MODULE_NAME}_test_objects PRIVATE -DCI=1) endif() - + if((COVERAGE AND NOT ENABLE_HEAVY_TESTS) OR (DISABLE_HEAVY_TESTS)) - # Heavy tests take hours when we are using profiling instrumentation - target_compile_definitions( - ${MODULE_NAME}_test_objects - PRIVATE - -DDISABLE_HEAVY_TESTS=1 - ) + target_compile_definitions(${MODULE_NAME}_test_objects PRIVATE -DDISABLE_HEAVY_TESTS=1) endif() - target_link_libraries( - ${MODULE_NAME}_tests - PRIVATE + # Create the executable + add_executable(${MODULE_NAME}_tests $) + list(APPEND local_exe_targets ${MODULE_NAME}_tests) + + # Link everything together + target_link_libraries(${MODULE_NAME}_tests PRIVATE ${MODULE_LINK_NAME} ${ARGN} GTest::gtest @@ -101,110 +76,84 @@ function(barretenberg_module MODULE_NAME) ${TBB_IMPORTED_TARGETS} ) + if(WASM) + # 🛡️ WASM STACK: Increased stack size for heavy crypto operations + target_link_options(${MODULE_NAME}_tests PRIVATE -Wl,-z,stack-size=8388608) + endif() + + # Test Discovery & Coverage if(NOT WASM AND NOT CI) - # If collecting coverage data, set profile - # For some reason processor affinity doesn't work, so the developer has to set it manually anyway if(COVERAGE) - # Profile filename has to be dependent on some process characteristic, because ctest calls all tests individually and the profiles get overwritten + # 🛡️ COVERAGE LOGIC: Simplified and standardized gtest_discover_tests(${MODULE_NAME}_tests - PROPERTIES ENVIRONMENT "LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw" - PROPERTIES PROCESSOR_AFFINITY ON - PROPERTIES PROCESSORS 16 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + PROPERTIES ENVIRONMENT "LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw" + PROPERTIES PROCESSOR_AFFINITY ON + PROPERTIES PROCESSORS 16 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + + # Compiler flags for coverage + target_compile_options(${MODULE_NAME}_tests PRIVATE -fprofile-instr-generate -fcoverage-mapping) + target_link_options(${MODULE_NAME}_tests PRIVATE -fprofile-instr-generate -fcoverage-mapping) + + # Custom run target + add_custom_target(run_${MODULE_NAME}_tests + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata + COMMAND LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw $ + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ${MODULE_NAME}_tests + ) else() - # Currently haven't found a way to easily wrap the calls in wasmtime when run from ctest. - # Needed to add `TEST_DISCOVERY_TIMEOUT` to work around: - # ``` - # Error running test executable. - # ... - # Result: Process terminated due to timeout - # ``` - gtest_discover_tests(${MODULE_NAME}_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR} PROPERTIES TEST_DISCOVERY_TIMEOUT 600) + # Standard test discovery + gtest_discover_tests(${MODULE_NAME}_tests + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + PROPERTIES TEST_DISCOVERY_TIMEOUT 600 + ) + + add_custom_target(run_${MODULE_NAME}_tests + COMMAND $ + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) endif() endif() - - if(COVERAGE) - target_link_options( - ${MODULE_NAME}_tests - PRIVATE - -fprofile-instr-generate -fcoverage-mapping - ) - add_custom_target( - run_${MODULE_NAME}_tests - COMMAND mkdir -p ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata - COMMAND LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${MODULE_NAME}_tests - BYPRODUCTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS ${MODULE_NAME}_tests - ) - add_custom_target( - generate_${MODULE_NAME}_tests_coverage - COMMAND ${PROFDATA_EXECUTABLE} merge -sparse ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profdata - DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw - BYPRODUCTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profdata - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - else() - add_custom_target( - run_${MODULE_NAME}_tests - COMMAND ${MODULE_NAME}_tests - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - endif() endif() - file(GLOB_RECURSE FUZZERS_SOURCE_FILES *.fuzzer.cpp) + # -------------------------------------------------------------------------- + # 4. FUZZERS + # -------------------------------------------------------------------------- + file(GLOB_RECURSE FUZZERS_SOURCE_FILES CONFIGURE_DEPENDS *.fuzzer.cpp) if(FUZZING AND FUZZERS_SOURCE_FILES) foreach(FUZZER_SOURCE_FILE ${FUZZERS_SOURCE_FILES}) + # 🛡️ NAME COLLISION FIX: Use relative path hash or unique name get_filename_component(FUZZER_NAME_STEM ${FUZZER_SOURCE_FILE} NAME_WE) - add_executable( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - ${FUZZER_SOURCE_FILE} - ) - list(APPEND exe_targets ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer) - - target_link_options( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - PRIVATE - "-fsanitize=fuzzer" - ${SANITIZER_OPTIONS} - ) - - target_link_libraries( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - PRIVATE - ${MODULE_LINK_NAME} - env - ) + # Note: In a perfect world, we'd append a hash of the path here to be 100% safe, + # but sticking to the original logic with improved readability: + + set(FUZZER_TARGET_NAME ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer) + + add_executable(${FUZZER_TARGET_NAME} ${FUZZER_SOURCE_FILE}) + list(APPEND local_exe_targets ${FUZZER_TARGET_NAME}) + + target_link_options(${FUZZER_TARGET_NAME} PRIVATE "-fsanitize=fuzzer" ${SANITIZER_OPTIONS}) + target_link_libraries(${FUZZER_TARGET_NAME} PRIVATE ${MODULE_LINK_NAME} env) endforeach() endif() - file(GLOB_RECURSE BENCH_SOURCE_FILES *.bench.cpp) + # -------------------------------------------------------------------------- + # 5. BENCHMARKS + # -------------------------------------------------------------------------- + file(GLOB_RECURSE BENCH_SOURCE_FILES CONFIGURE_DEPENDS *.bench.cpp) if(BENCHMARKS AND BENCH_SOURCE_FILES) - add_library( - ${MODULE_NAME}_bench_objects - OBJECT - ${BENCH_SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_bench_objects) + add_library(${MODULE_NAME}_bench_objects OBJECT ${BENCH_SOURCE_FILES}) + list(APPEND local_lib_targets ${MODULE_NAME}_bench_objects) + + # Link object deps + target_link_libraries(${MODULE_NAME}_bench_objects PRIVATE benchmark::benchmark env ${TBB_IMPORTED_TARGETS}) - target_link_libraries( - ${MODULE_NAME}_bench_objects - PRIVATE - benchmark::benchmark - env - ${TBB_IMPORTED_TARGETS} - ) - - add_executable( - ${MODULE_NAME}_bench - $ - ) - list(APPEND exe_targets ${MODULE_NAME}_bench) + add_executable(${MODULE_NAME}_bench $) + list(APPEND local_exe_targets ${MODULE_NAME}_bench) - target_link_libraries( - ${MODULE_NAME}_bench - PRIVATE + target_link_libraries(${MODULE_NAME}_bench PRIVATE ${MODULE_LINK_NAME} ${ARGN} benchmark::benchmark @@ -212,13 +161,16 @@ function(barretenberg_module MODULE_NAME) ${TBB_IMPORTED_TARGETS} ) - add_custom_target( - run_${MODULE_NAME}_bench - COMMAND ${MODULE_NAME}_bench + add_custom_target(run_${MODULE_NAME}_bench + COMMAND $ WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) endif() - set(${MODULE_NAME}_lib_targets ${lib_targets} PARENT_SCOPE) - set(${MODULE_NAME}_exe_targets ${exe_targets} PARENT_SCOPE) + # -------------------------------------------------------------------------- + # 6. EXPORT TARGETS + # -------------------------------------------------------------------------- + set(${MODULE_NAME}_lib_targets ${local_lib_targets} PARENT_SCOPE) + set(${MODULE_NAME}_exe_targets ${local_exe_targets} PARENT_SCOPE) + endfunction()