diff --git a/FairSoftConfig.cmake b/FairSoftConfig.cmake index 115c11fc..497c3245 100644 --- a/FairSoftConfig.cmake +++ b/FairSoftConfig.cmake @@ -37,6 +37,23 @@ set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install" CACHE PATH "Install prefi # set(GEANT4MT OFF CACHE BOOL "Build Geant4 in multi-threading mode" FORCE) # set(GEANT4MT ON CACHE BOOL "Build Geant4 in multi-threading mode" FORCE) +# +# CI Build Mode (default OFF) +# +# Enable this to reduce peak disk usage during builds. When enabled: +# - Packages are built sequentially (one at a time) instead of in parallel +# - Each package's build and source directories are removed after installation +# - Disk usage is logged (space reclaimed + total build tree size) +# +# The -j argument still parallelizes compilation within each package build. +# Trade-offs: +# - Builds take longer due to package serialization +# - Incremental rebuilds after failure are longer (removed packages must fully rebuild) +# Recommended for CI environments or systems with limited storage. +# +# set(CI_BUILD_MODE OFF CACHE BOOL "Serialize builds and remove package dirs after install" FORCE) +# set(CI_BUILD_MODE ON CACHE BOOL "Serialize builds and remove package dirs after install" FORCE) + # # Python # diff --git a/cmake/legacy.cmake b/cmake/legacy.cmake index 5c89f7cc..6aa96261 100644 --- a/cmake/legacy.cmake +++ b/cmake/legacy.cmake @@ -551,6 +551,44 @@ add_custom_target(source-cache COMMENT "Creating source cache at ${tarfile}" ) +# CI_BUILD_MODE: Serialize builds and remove package directories after install +if(NOT DEFINED CI_BUILD_MODE) + set(CI_BUILD_MODE OFF) +endif() + +if(CI_BUILD_MODE) + foreach(pkg IN LISTS packages) + ExternalProject_Add_Step(${pkg} reclaim_diskspace + COMMAND ${CMAKE_COMMAND} + -DPACKAGE_NAME=${pkg} + -DPKG_BUILD_DIR=${CMAKE_BINARY_DIR}/Build/${pkg} + -DPKG_SOURCE_DIR=${CMAKE_BINARY_DIR}/Source/${pkg} + -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} + -P ${CMAKE_SOURCE_DIR}/cmake/reclaim-diskspace.cmake + COMMENT "Removing ${pkg} package directories to reclaim disk space" + DEPENDEES install + ALWAYS OFF + ) + endforeach() + + # Serialize package builds to reduce peak disk usage in CI environments. + # This ensures only one package builds at a time, preventing multiple large + # package directories from existing simultaneously. + # Exclude optional packages (EXCLUDE_FROM_ALL) from the serial chain. + set(prev_pkg "") + foreach(pkg IN LISTS packages) + get_target_property(is_excluded ${pkg} EXCLUDE_FROM_ALL) + if(is_excluded) + continue() + endif() + if(prev_pkg) + ExternalProject_Add_StepDependencies(${pkg} configure ${prev_pkg}) + endif() + set(prev_pkg ${pkg}) + endforeach() + unset(prev_pkg) +endif() + include(CTest) foreach(ver IN ITEMS 18.6 18.8 19.0) @@ -604,6 +642,12 @@ if(SOURCE_CACHE) else() message(STATUS " ${Cyan}SOURCE CACHE${CR} using upstream URLs (generate cache by building target 'source-cache' and pass via ${BMagenta}-DSOURCE_CACHE=...${CR})") endif() +message(STATUS " ") +if(CI_BUILD_MODE) + message(STATUS " ${Cyan}CI_BUILD_MODE${CR} ${BGreen}ON${CR} (serialized builds, package directories removed after install)") +else() + message(STATUS " ${Cyan}CI_BUILD_MODE${CR} ${BGreen}OFF${CR} (enable with ${BMagenta}-DCI_BUILD_MODE=ON${CR} to reduce disk usage)") +endif() if(CMAKE_OSX_SYSROOT) message(STATUS " ") message(STATUS " ${Cyan}OSX_SYSROOT${CR} ${BGreen}${CMAKE_OSX_SYSROOT}${CR} (change with ${BMagenta}-DCMAKE_OSX_SYSROOT=...${CR})") diff --git a/cmake/reclaim-diskspace.cmake b/cmake/reclaim-diskspace.cmake new file mode 100644 index 00000000..b7bc3b13 --- /dev/null +++ b/cmake/reclaim-diskspace.cmake @@ -0,0 +1,64 @@ +# reclaim-diskspace.cmake +# Remove package build/source directories after install to reclaim disk space +# Expected variables: PACKAGE_NAME, PKG_BUILD_DIR, PKG_SOURCE_DIR, CMAKE_BINARY_DIR + +if(NOT DEFINED PACKAGE_NAME OR NOT DEFINED PKG_BUILD_DIR OR NOT DEFINED PKG_SOURCE_DIR) + message(FATAL_ERROR "PACKAGE_NAME, PKG_BUILD_DIR, and PKG_SOURCE_DIR must be defined") +endif() + +# Helper function to measure directory size in MB +function(measure_dir_size_mb dir out_var out_success) + set(${out_success} FALSE PARENT_SCOPE) + set(${out_var} 0 PARENT_SCOPE) + if(UNIX) + find_program(DU_EXECUTABLE du) + if(DU_EXECUTABLE) + execute_process( + COMMAND ${DU_EXECUTABLE} -sk "${dir}" + OUTPUT_VARIABLE du_output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(du_output) + string(REGEX REPLACE "^([0-9]+).*" "\\1" size_kb "${du_output}") + math(EXPR size_mb "${size_kb} / 1024") + set(${out_var} ${size_mb} PARENT_SCOPE) + set(${out_success} TRUE PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +# Measure sizes before removal +measure_dir_size_mb("${PKG_BUILD_DIR}" build_mb build_ok) +measure_dir_size_mb("${PKG_SOURCE_DIR}" source_mb source_ok) + +if(build_ok AND source_ok) + math(EXPR total_mb "${build_mb} + ${source_mb}") + message(STATUS "[${PACKAGE_NAME}] Reclaiming ~${total_mb} MB (build: ${build_mb}, source: ${source_mb})") +elseif(build_ok) + message(STATUS "[${PACKAGE_NAME}] Reclaiming ~${build_mb} MB") +elseif(source_ok) + message(STATUS "[${PACKAGE_NAME}] Reclaiming ~${source_mb} MB") +else() + message(STATUS "[${PACKAGE_NAME}] Removing package directories...") +endif() + +# Remove package directories +execute_process(COMMAND ${CMAKE_COMMAND} -E rm -rf "${PKG_BUILD_DIR}" RESULT_VARIABLE build_rm) +execute_process(COMMAND ${CMAKE_COMMAND} -E rm -rf "${PKG_SOURCE_DIR}" RESULT_VARIABLE source_rm) + +if(NOT build_rm EQUAL 0) + message(WARNING "[${PACKAGE_NAME}] Failed to remove package build directory") +endif() +if(NOT source_rm EQUAL 0) + message(WARNING "[${PACKAGE_NAME}] Failed to remove package source directory") +endif() + +# Report total build tree size +if(DEFINED CMAKE_BINARY_DIR) + measure_dir_size_mb("${CMAKE_BINARY_DIR}" tree_mb tree_ok) + if(tree_ok) + message(STATUS "[${PACKAGE_NAME}] Build tree size: ${tree_mb} MB") + endif() +endif()