From 3e995a5bb49fe78f36af6e3ceb77f8f1964f98c3 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Wed, 2 Sep 2026 10:24:48 -0500 Subject: [PATCH] build: fix MKL version detection for oneAPI and fail fast on a bad Boost tree FindLapack.cmake parsed the MKL version with a single-space pattern ("#define __INTEL_MKL__ ([0-9]+)"). oneAPI column-aligns the values in mkl_version.h, so the pattern never matched, MKL_VERSION ended up holding the raw header lines, and the unquoted if() checks died with CMake Error at config/FindLapack.cmake:159 (if): Unknown arguments specified as soon as MKLROOT was set in the environment (reported in #117 for a Spack build on a machine with oneAPI 2025 installed). - Match the whitespace as [ \t]+ and extract the version with REGEX MATCH. - Decide the library layout by version comparison (layered libraries since 10.0, lib/intel64 or lib since 10.3) instead of patterns that only knew 10.x and 11.x, so the year-numbered releases (2017 ... oneAPI 2025) get the layered libraries rather than the pre-10 -lmkl -lguide set. - Use lib/ when lib/intel64 does not exist (oneAPI 2024+), add -ldl on Linux, warn and assume a current release when no version can be read, and fix the $ENV{mkl_home} typo in the include search. FindBoostSrc.cmake: when Boost_SRC_DIR points at a directory without boost/version.hpp (the first failure in #117, an empty resource directory), stop with one FATAL_ERROR naming the directory and the options, instead of a file(READ) error, three MATH() errors and "Boost Version: ERROR_ERROR_ERROR". Refs #117 Co-Authored-By: Claude Fable 5.1 --- cmake/FindBoostSrc.cmake | 21 ++++++++++- cmake/FindLapack.cmake | 80 +++++++++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/cmake/FindBoostSrc.cmake b/cmake/FindBoostSrc.cmake index a9029a24b..040c8a9d0 100644 --- a/cmake/FindBoostSrc.cmake +++ b/cmake/FindBoostSrc.cmake @@ -85,9 +85,26 @@ endif(Boost_ROOT_DIR) if(Boost_INCLUDE_DIR) set(_boost_VERSION 0) set(_boost_LIB_VERSION "") + # Stop here with one clear message if the directory is not a Boost source + # tree (typically Boost_SRC_DIR pointing at an empty or wrong directory). + # Otherwise file(READ) fails, the MATH() calls below error out on empty + # strings, and the run ends with "Boost Version: ERROR_ERROR_ERROR". + if(NOT EXISTS "${Boost_INCLUDE_DIR}/boost/version.hpp") + message(FATAL_ERROR + "Boost source tree at ${Boost_INCLUDE_DIR} does not contain boost/version.hpp. " + "Point Boost_SRC_DIR (or BOOST_ROOT) at an unpacked Boost source release, " + "leave it unset to let ALPS download one, or set ALPS_USE_SYSTEM_BOOST=ON " + "to build against an installed Boost.") + endif() file(READ "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS) - string(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" _boost_VERSION "${_boost_VERSION_HPP_CONTENTS}") - string(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" _boost_LIB_VERSION "${_boost_VERSION_HPP_CONTENTS}") + if(NOT _boost_VERSION_HPP_CONTENTS MATCHES "#define BOOST_VERSION ([0-9]+)") + message(FATAL_ERROR + "Could not read BOOST_VERSION from ${Boost_INCLUDE_DIR}/boost/version.hpp.") + endif() + set(_boost_VERSION "${CMAKE_MATCH_1}") + if(_boost_VERSION_HPP_CONTENTS MATCHES "#define BOOST_LIB_VERSION \"([0-9_]+)\"") + set(_boost_LIB_VERSION "${CMAKE_MATCH_1}") + endif() set(Boost_LIB_VERSION ${_boost_LIB_VERSION} CACHE INTERNAL "The library version string for boost libraries") set(Boost_VERSION ${_boost_VERSION} CACHE INTERNAL "The version number for boost libraries") MATH(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") diff --git a/cmake/FindLapack.cmake b/cmake/FindLapack.cmake index 9a295913c..3dc9e5dbe 100755 --- a/cmake/FindLapack.cmake +++ b/cmake/FindLapack.cmake @@ -115,14 +115,56 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) endif() if(mkl_home MATCHES "mkl") - file( STRINGS "${mkl_home}/include/mkl.h" _mkl_h_content REGEX "__INTEL_MKL" ) - if(NOT _mkl_h_content MATCHES "__INTEL_MKL__") - file( STRINGS "${mkl_home}/include/mkl_version.h" _mkl_h_content REGEX "__INTEL_MKL" ) + # Read the MKL version from the headers. MKL >= 11.1 keeps the macros in + # mkl_version.h, older releases in mkl.h. oneAPI column-aligns the values + # ("#define __INTEL_MKL__ 2025"), so the whitespace between macro + # name and value has to be matched as [ \t]+ -- a single-space pattern + # silently fails and leaves the raw header lines in MKL_VERSION, which + # then blows up the unquoted if() checks below with "Unknown arguments". + set(_mkl_h_content "") + foreach(_mkl_header mkl_version.h mkl.h) + if(EXISTS "${mkl_home}/include/${_mkl_header}") + file(STRINGS "${mkl_home}/include/${_mkl_header}" _mkl_h_content + REGEX "#define[ \t]+__INTEL_MKL(__|_MINOR__|_UPDATE__)[ \t]+[0-9]+") + if(_mkl_h_content) + break() + endif() + endif() + endforeach() + unset(_mkl_header) + + set(MKL_VERSION "") + if("${_mkl_h_content}" MATCHES "#define[ \t]+__INTEL_MKL__[ \t]+([0-9]+)") + set(MKL_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(MKL_VERSION_MINOR 0) + set(MKL_VERSION_UPDATE 0) + if("${_mkl_h_content}" MATCHES "#define[ \t]+__INTEL_MKL_MINOR__[ \t]+([0-9]+)") + set(MKL_VERSION_MINOR "${CMAKE_MATCH_1}") + endif() + if("${_mkl_h_content}" MATCHES "#define[ \t]+__INTEL_MKL_UPDATE__[ \t]+([0-9]+)") + set(MKL_VERSION_UPDATE "${CMAKE_MATCH_1}") + endif() + set(MKL_VERSION "${MKL_VERSION_MAJOR}.${MKL_VERSION_MINOR}.${MKL_VERSION_UPDATE}") + else() + message(WARNING "MKL found in ${mkl_home}, but its version could not be read from " + "include/mkl_version.h or include/mkl.h; assuming a current release.") + endif() + unset(_mkl_h_content) + + # MKL 10.0 introduced the layered libraries (mkl_intel_lp64 / + # mkl_sequential|mkl_*_thread / mkl_core); every later release, including + # the year-numbered ones (2017 ... oneAPI 2025), uses them. Only + # pre-10.0 releases need the monolithic -lmkl -lguide. MKL 10.3 moved + # the libraries from lib/em64t to lib/intel64 (Linux) and lib (macOS). + # An unparsable version is treated as a current release. + set(_mkl_layered TRUE) + set(_mkl_flat_libdir TRUE) + if(MKL_VERSION AND MKL_VERSION VERSION_LESS 10.0) + set(_mkl_layered FALSE) + endif() + if(MKL_VERSION AND MKL_VERSION VERSION_LESS 10.3) + set(_mkl_flat_libdir FALSE) endif() - string(REGEX REPLACE ".*#define __INTEL_MKL__ ([0-9]+).*" "\\1" MKL_VERSION_MAJOR "${_mkl_h_content}") - string(REGEX REPLACE ".*#define __INTEL_MKL_MINOR__ ([0-9]+).*" "\\1" MKL_VERSION_MINOR "${_mkl_h_content}") - string(REGEX REPLACE ".*#define __INTEL_MKL_UPDATE__ ([0-9]+).*" "\\1" MKL_VERSION_UPDATE "${_mkl_h_content}") - set(MKL_VERSION "${MKL_VERSION_MAJOR}.${MKL_VERSION_MINOR}.${MKL_VERSION_UPDATE}") # STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MKL_VERSION ${mkl_home}) # set(MKL_VERSION_RAW ${MKL_VERSION}) @@ -156,7 +198,7 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) set(MKL_CORE -lmkl_gnu_thread -lmkl_core -lgfortran) endif() else() - if(${MKL_VERSION} MATCHES "1[0-1]\\.[0-3]\\.[0-9]+") + if(_mkl_layered) set(MKL_CORE -lmkl_sequential -lmkl_core) else() # MKL < 10.0 set(MKL_CORE -lmkl_lapack -lmkl -lguide) @@ -164,7 +206,7 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) endif() # basic data type model interface # - assuming ILP32 or LP64 - if(${MKL_VERSION} MATCHES "1[0-1]\\.[0-3]\\.[0-9]+") + if(_mkl_layered) if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "ia64") set(MKL_INTERFACE -lmkl_intel_lp64) elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "i386" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "i686") @@ -177,7 +219,7 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) endif() # MKL library path if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - if(${MKL_VERSION} MATCHES "11\\.[0-9]\\.[0-9]+" OR ${MKL_VERSION} MATCHES "10\\.3\\.[0-9]+") + if(_mkl_flat_libdir) set(MKL_LIBRARY_PATH -L${mkl_home}/lib) else() # MKL < 10.3 if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") @@ -189,9 +231,15 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) endif() endif() elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - if(${MKL_VERSION} MATCHES "11\\.[0-9]\\.[0-9]+" OR ${MKL_VERSION} MATCHES "10\\.3\\.[0-9]+") + if(_mkl_flat_libdir) if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") - set(MKL_LIBRARY_PATH -L${mkl_home}/lib/intel64) + # oneAPI 2024+ ships the libraries directly in lib/ and keeps + # lib/intel64 at most as a compatibility symlink. + if(IS_DIRECTORY "${mkl_home}/lib/intel64") + set(MKL_LIBRARY_PATH -L${mkl_home}/lib/intel64) + else() + set(MKL_LIBRARY_PATH -L${mkl_home}/lib) + endif() elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "i386" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "i686") set(MKL_LIBRARY_PATH -L${mkl_home}/lib/ia32) else() @@ -211,11 +259,17 @@ if(NOT HAVE_MKL AND NOT LAPACK_LIBRARY_INIT) endif() # combine together set(LAPACK_LIBRARY ${MKL_LIBRARY_PATH} ${MKL_INTERFACE} ${MKL_CORE} ${CMAKE_THREAD_LIBS_INIT} -lm) + if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + # mkl_core dlopen()s its CPU-specific kernels at run time + list(APPEND LAPACK_LIBRARY -ldl) + endif() # unset local variables unset(MKL_LIBRARY_PATH) unset(MKL_INTERFACE) unset(MKL_CORE) + unset(_mkl_layered) + unset(_mkl_flat_libdir) set(BLAS_LIBRARY "") set(HAVE_MKL TRUE) @@ -240,7 +294,7 @@ IF(HAVE_MKL) message(STATUS "Found intel/mkl library") set(LAPACK_LIBRARY_INIT 1) set(BLAS_LIBRARY_INIT 1) - set(MKL_INC_PATHS $ENV{MKLROOT}/include $ENV{mkl_home}/include ${MKL_PATHS}) + set(MKL_INC_PATHS $ENV{MKLROOT}/include ${mkl_home}/include ${MKL_PATHS}) find_path(MKL_INCLUDE_DIR mkl.h ${MKL_INC_PATHS}) include_directories(${MKL_INCLUDE_DIR}) set(ALPS_HAVE_MKL 1) # MKL flag set in alps/config.h