From 0c4b10bb1c0174c49a9529b886e978eee0d0e6f1 Mon Sep 17 00:00:00 2001
From: Lars Bilke <lars.bilke@ufz.de>
Date: Wed, 22 Feb 2023 10:47:08 +0100
Subject: [PATCH] [cmake] Cache external dependencies in CPM_SOURCE_CACHE.

If CPM_SOURCE_CACHE is set and outside the build directory.

Full build is cached in $CPM_SOURCE_CACHE/_ext/[NAME]/[HASH]

Hash is based on all parameters given to build the dependency (version
info, cmake options).
---
 scripts/ci/jobs/build-linux.yml               |  2 +-
 scripts/cmake/BuildExternalProject.cmake      | 28 +++++++++++++++----
 .../cmake/DependenciesExternalProject.cmake   | 27 ++++++++----------
 3 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/scripts/ci/jobs/build-linux.yml b/scripts/ci/jobs/build-linux.yml
index a42595fc327..daeabb0cda0 100644
--- a/scripts/ci/jobs/build-linux.yml
+++ b/scripts/ci/jobs/build-linux.yml
@@ -67,7 +67,7 @@ build linux debug with sanitizers:
     - .template-build-linux
     - .test-artifacts
     - .rules-master-manual
-  tags: [envinf2-shell]
+  tags: [envinf23, shell]
   needs: [meta]
   variables:
     BUILD_PACKAGE: "false"
diff --git a/scripts/cmake/BuildExternalProject.cmake b/scripts/cmake/BuildExternalProject.cmake
index 964e394b562..40ac7a16a61 100644
--- a/scripts/cmake/BuildExternalProject.cmake
+++ b/scripts/cmake/BuildExternalProject.cmake
@@ -1,18 +1,18 @@
 # Modified from
 # https://github.com/Sbte/BuildExternalProject/commit/ce1a70996aa538aac17a6faf07db487c3a238838
 macro(BuildExternalProject_find_package target)
-    set(build_dir ${PROJECT_BINARY_DIR}/_ext/${target})
 
     # Set CMake prefix path so we can look there for the module
     set(_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH})
     mark_as_advanced(_CMAKE_PREFIX_PATH)
-    list(APPEND CMAKE_PREFIX_PATH ${build_dir})
+    list(APPEND CMAKE_PREFIX_PATH ${build_dir_${target}})
 
     find_package(${target} MODULE QUIET)
     if(NOT ${target}_FOUND)
         # Look for config version if there was no module
         find_package(
-            ${target} CONFIG REQUIRED HINTS ${build_dir} NO_DEFAULT_PATH
+            ${target} CONFIG REQUIRED HINTS ${build_dir_${target}}
+            NO_DEFAULT_PATH
         )
     endif()
 
@@ -22,14 +22,28 @@ macro(BuildExternalProject_find_package target)
 endmacro()
 
 function(BuildExternalProject target)
+
+    message(STATUS "┌─ BuildExternalProject ${target}")
+    list(APPEND CMAKE_MESSAGE_INDENT "│    ")
+
     set(build_dir ${PROJECT_BINARY_DIR}/_ext/${target})
+    string(REPLACE ";" " " ARGN_STRING "${ARGN}")
+
+    if(CPM_SOURCE_CACHE)
+        cmake_path(
+            IS_PREFIX PROJECT_BINARY_DIR "${CPM_SOURCE_CACHE}" _is_inside_build
+        )
+        if(NOT _is_inside_build)
+            string(SHA256 _hash "${ARGN_STRING}")
+            set(build_dir "${CPM_SOURCE_CACHE}/_ext/${target}/${_hash}")
+        endif()
+    endif()
 
-    message(STATUS "Building ${target}")
+    message(STATUS "Building ${target} in ${build_dir}")
+    set(build_dir_${target} "${build_dir}" CACHE INTERNAL "")
 
     file(MAKE_DIRECTORY ${build_dir})
 
-    string(REPLACE ";" " " ARGN_STRING "${ARGN}")
-
     set(CMAKE_LIST_CONTENT
         "
     cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
@@ -73,6 +87,8 @@ function(BuildExternalProject target)
         STATUS
             "Finished building ${target}. Logs in ${build_dir}/src/${target}-stamp"
     )
+    list(POP_BACK CMAKE_MESSAGE_INDENT)
+    message(STATUS "└─ End BuildExternalProject ${_target}")
 endfunction()
 
 function(BuildExternalProject_configure build_dir)
diff --git a/scripts/cmake/DependenciesExternalProject.cmake b/scripts/cmake/DependenciesExternalProject.cmake
index d7cdb420d8f..c4fb852303f 100644
--- a/scripts/cmake/DependenciesExternalProject.cmake
+++ b/scripts/cmake/DependenciesExternalProject.cmake
@@ -3,7 +3,7 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.24)
 endif()
 
 # Build dependencies via ExternalProject_Add() at configure time in
-# ${PROJECT_BINARY_DIR}/_ext
+# ${PROJECT_BINARY_DIR}/_ext or in $CPM_SOURCE_CACHE/_ext
 include(BuildExternalProject)
 
 set(OGS_EXTERNAL_DEPENDENCIES_CACHE ""
@@ -95,7 +95,7 @@ if(OGS_USE_MFRONT)
             STATUS
                 "ExternalProject_Add(): added package TFEL@rliv-${ogs.minimum_version.tfel-rliv}"
         )
-        set(TFELHOME ${PROJECT_BINARY_DIR}/_ext/TFEL CACHE PATH "" FORCE)
+        set(TFELHOME ${build_dir_TFEL} CACHE PATH "" FORCE)
     endif()
 endif()
 
@@ -197,7 +197,7 @@ if(OGS_USE_LIS)
             STATUS
                 "ExternalProject_Add(): added package LIS@${ogs.minimum_version.lis}"
         )
-        set(ENV{LIS_ROOT_DIR} ${PROJECT_BINARY_DIR}/_ext/LIS)
+        set(ENV{LIS_ROOT_DIR} ${build_dir_LIS})
         find_package(LIS REQUIRED)
     endif()
 endif()
@@ -229,7 +229,7 @@ if(NOT ZLIB_FOUND)
     if(WIN32)
         # requires CMake 3.24 to be effective:
         set(ZLIB_USE_STATIC_LIBS "ON")
-        set(ZLIB_ROOT ${PROJECT_BINARY_DIR}/_ext/ZLIB)
+        set(ZLIB_ROOT ${build_dir_ZLIB})
     endif()
     set(_EXT_LIBS ${_EXT_LIBS} ZLIB CACHE INTERNAL "")
     BuildExternalProject_find_package(ZLIB)
@@ -251,8 +251,8 @@ set(_hdf5_options
     "-DBUILD_TESTING=OFF"
     "-DHDF5_ENABLE_Z_LIB_SUPPORT=ON"
 )
-if("${ZLIB_INCLUDE_DIRS}" MATCHES "${PROJECT_BINARY_DIR}/_ext/ZLIB")
-    list(APPEND _hdf5_options "-DZLIB_ROOT=${PROJECT_BINARY_DIR}/_ext/ZLIB")
+if("${ZLIB_INCLUDE_DIRS}" MATCHES "${build_dir_ZLIB}")
+    list(APPEND _hdf5_options "-DZLIB_ROOT=${build_dir_ZLIB}")
     if(WIN32)
         list(APPEND _hdf5_options "-DZLIB_USE_STATIC_LIBS=ON")
     endif()
@@ -321,10 +321,10 @@ list(APPEND VTK_OPTIONS "-DBUILD_SHARED_LIBS=OFF"
      "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
 )
 message(STATUS "VTK_OPTIONS: ${VTK_OPTIONS}")
-if(OGS_USE_PETSC AND EXISTS ${PROJECT_BINARY_DIR}/_ext/HDF5)
+if(OGS_USE_PETSC AND EXISTS ${build_dir_HDF5})
     # Use local hdf5 build
     list(APPEND VTK_OPTIONS "-DVTK_MODULE_USE_EXTERNAL_VTK_hdf5=ON"
-         "-DHDF5_ROOT=${PROJECT_BINARY_DIR}/_ext/HDF5"
+         "-DHDF5_ROOT=${build_dir_HDF5}"
     )
 endif()
 
@@ -351,14 +351,12 @@ elseif(NOT OGS_BUILD_VTK AND NOT OGS_USE_MKL)
     find_package(VTK ${ogs.minimum_version.vtk} COMPONENTS ${VTK_COMPONENTS})
 endif()
 if(NOT VTK_FOUND)
-
-    if("${OGS_EXTERNAL_DEPENDENCIES_CACHE}" STREQUAL ""
-       AND NOT EXISTS "${PROJECT_BINARY_DIR}/_ext/VTK/src/VTK"
-    )
+    if(APPLE AND "${OGS_EXTERNAL_DEPENDENCIES_CACHE}" STREQUAL "")
         # Fixes https://stackoverflow.com/questions/9894961 on vismac05:
         set(_loguru_patch PATCH_COMMAND git apply
                           "${PROJECT_SOURCE_DIR}/scripts/cmake/loguru.patch"
         )
+        message(DEBUG "Applying VTK loguru patch")
     endif()
     BuildExternalProject(
         VTK ${_vtk_source} CMAKE_ARGS ${VTK_OPTIONS} ${_defaultCMakeArgs}
@@ -373,8 +371,7 @@ endif()
 
 # append RPATHs
 foreach(lib ${_EXT_LIBS})
-    set(CMAKE_BUILD_RPATH
-        ${CMAKE_BUILD_RPATH} ${PROJECT_BINARY_DIR}/_ext/${lib}/lib
-        ${PROJECT_BINARY_DIR}/_ext/${lib}/lib64
+    set(CMAKE_BUILD_RPATH ${CMAKE_BUILD_RPATH} ${build_dir_${lib}}/lib
+                          {build_dir_${lib}}/lib64
     )
 endforeach()
-- 
GitLab