Skip to content

Commit fb505c2

Browse files
committed
Rewrite FindMKL.cmake
1 parent a9132cb commit fb505c2

5 files changed

Lines changed: 248 additions & 116 deletions

File tree

CMakeLists.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ if(DEBUG_INFO)
335335
endif()
336336

337337
if(ENABLE_MPI)
338+
if(NOT CMAKE_CROSSCOMPILING)
339+
# FindMPI runs a probe executable to determine the MPI library version,
340+
# which FindMKL.cmake uses to auto-detect the BLACS interface.
341+
# When cross compiling, assume users know what they are doing.
342+
set(MPI_DETERMINE_LIBRARY_VERSION TRUE)
343+
endif()
338344
find_package(MPI COMPONENTS CXX REQUIRED)
339345
abacus_add_feature_definitions(__MPI)
340346
endif()
@@ -542,9 +548,7 @@ if(USE_KML)
542548

543549
find_package(KML REQUIRED COMPONENTS ${_kml_components})
544550
abacus_add_feature_definitions(__KML)
545-
elseif(MKLROOT)
546-
set(MKL_INTERFACE lp64)
547-
set(ENABLE_SCALAPACK ON)
551+
elseif(MKLROOT OR MKL_ROOT)
548552
find_package(MKL REQUIRED)
549553
abacus_add_feature_definitions(__MKL)
550554
elseif(NOT USE_SW)

cmake/CollectBuildInfoVars.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ else()
165165
set(ABACUS_ELPA_VERSION "no")
166166
endif()
167167

168-
if(MKLROOT)
168+
if(MKL_FOUND)
169169
set(ABACUS_MKL_SUPPORT "yes (version unknown)")
170170
find_path(MKL_VERSION_HEADER mkl_version.h PATHS ${MKL_INCLUDE} NO_DEFAULT_PATH)
171171
if(MKL_VERSION_HEADER)

cmake/modules/FindMKL.cmake

Lines changed: 231 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,254 @@
1-
# - Find mkl
2-
# Find the native MKL headers and libraries.
1+
# Find the oneMKL components used by ABACUS.
32
#
4-
# MKL_INCLUDE - where to find mkl.h, etc.
5-
# MKL_FOUND - True if mkl found.
6-
7-
# find_package(MKL NO_MODULE) # try using official module first
8-
if(NOT TARGET MKL::MKL)
9-
10-
find_path(MKL_INCLUDE mkl_service.h HINTS ${MKLROOT}/include)
11-
12-
find_library(MKL_CORE NAMES mkl_core HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
13-
if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
14-
find_library(MKL_INTERFACE_LIB NAMES mkl_intel_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
15-
find_library(MKL_THREAD NAMES mkl_intel_thread HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
16-
find_library(MKL_IOMP5 NAMES iomp5
17-
HINTS ENV CMPLR_ROOT
18-
PATH_SUFFIXES lib lib/intel64 linux/compiler/lib/intel64_lin
19-
)
3+
# ABACUS uses the LP64 BLAS/LAPACK, FFTW3 and, with MPI, BLACS/ScaLAPACK
4+
# interfaces directly. This module therefore provides complete link closures:
5+
#
6+
# abacus::mkl BLAS, LAPACK and FFTW3 compatibility interfaces
7+
# abacus::mkl_scalapack abacus::mkl plus BLACS, ScaLAPACK and MPI
8+
# (available only when ENABLE_MPI is ON)
9+
#
10+
# Search roots: MKLROOT, MKL_ROOT, or the MKLROOT environment variable.
11+
#
12+
# Optional cache variables:
13+
# MKL_LINK AUTO (default), static, or dynamic
14+
# MKL_THREADING AUTO (default), sequential, gnu_thread, or intel_thread
15+
# MKL_MPI AUTO (default), openmpi, intelmpi, or mpich
16+
#
17+
18+
include(FindPackageHandleStandardArgs)
19+
20+
# Reuse targets configured by this module. ABACUS deliberately keeps its
21+
# adapter targets separate from the provider-owned MKL:: namespace.
22+
if(TARGET abacus::mkl)
23+
set(MKL_LIBRARIES abacus::mkl)
24+
if(ENABLE_MPI)
25+
if(NOT TARGET abacus::mkl_scalapack)
26+
message(FATAL_ERROR
27+
"The existing ABACUS MKL configuration lacks abacus::mkl_scalapack "
28+
"for an MPI build.")
29+
endif()
30+
set(MKL_LIBRARIES abacus::mkl_scalapack)
31+
endif()
32+
set(MKL_FOUND TRUE)
33+
return()
34+
endif()
35+
36+
# ABACUS declares and calls LP64 Fortran-style symbols directly, so ILP64 is not
37+
# ABI-compatible with its integer arguments.
38+
if(DEFINED MKL_INTERFACE)
39+
string(TOLOWER "${MKL_INTERFACE}" _mkl_integer_interface)
40+
if(NOT _mkl_integer_interface STREQUAL "lp64")
41+
message(FATAL_ERROR "ABACUS supports only MKL_INTERFACE=lp64.")
42+
endif()
43+
endif()
44+
45+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
46+
set(_mkl_interface_name mkl_gf_lp64)
2047
else()
21-
find_library(MKL_INTERFACE_LIB NAMES mkl_gf_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
22-
find_library(MKL_THREAD NAMES mkl_gnu_thread HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
23-
# With GCC we use system-installed GNU OpenMP
48+
set(_mkl_interface_name mkl_intel_lp64)
49+
endif()
50+
51+
set(_mkl_root_hints "${MKLROOT}" "${MKL_ROOT}" "$ENV{MKLROOT}")
52+
list(REMOVE_ITEM _mkl_root_hints "")
53+
list(REMOVE_DUPLICATES _mkl_root_hints)
54+
55+
set(MKL_LINK AUTO CACHE STRING "oneMKL link mode: AUTO, static, or dynamic")
56+
set_property(CACHE MKL_LINK PROPERTY STRINGS AUTO static dynamic)
57+
string(TOLOWER "${MKL_LINK}" _mkl_link)
58+
if(NOT _mkl_link MATCHES "^(auto|static|dynamic)$")
59+
message(FATAL_ERROR "MKL_LINK must be AUTO, static, or dynamic.")
60+
endif()
61+
62+
if(NOT DEFINED MKL_THREADING)
63+
set(MKL_THREADING AUTO CACHE STRING
64+
"oneMKL threading layer: AUTO, sequential, gnu_thread, or intel_thread")
65+
set_property(CACHE MKL_THREADING PROPERTY STRINGS AUTO sequential gnu_thread intel_thread)
66+
endif()
67+
string(TOLOWER "${MKL_THREADING}" _mkl_threading)
68+
if(_mkl_threading STREQUAL "auto")
69+
set(_mkl_threading sequential)
70+
if(USE_OPENMP)
71+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
72+
set(_mkl_threading gnu_thread)
73+
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
74+
set(_mkl_threading intel_thread)
75+
endif()
76+
endif()
77+
elseif(NOT _mkl_threading MATCHES "^(sequential|gnu_thread|intel_thread)$")
78+
message(FATAL_ERROR "MKL_THREADING must be AUTO, sequential, gnu_thread, or intel_thread.")
2479
endif()
2580

2681
if(ENABLE_MPI)
27-
execute_process(COMMAND ${MPI_CXX_COMPILER} --showme:version
28-
OUTPUT_VARIABLE MPI_VER_OUT
29-
ERROR_VARIABLE MPI_VER_ERR)
30-
if(MPI_VER_OUT MATCHES "Open MPI" OR MPI_VER_ERR MATCHES "Open MPI")
31-
set(MKL_BLACS_LIB_NAME "mkl_blacs_openmpi_lp64")
32-
else()
33-
set(MKL_BLACS_LIB_NAME "mkl_blacs_intelmpi_lp64")
82+
set(MKL_MPI AUTO CACHE STRING "oneMKL MPI interface: AUTO, openmpi, intelmpi, or mpich")
83+
set_property(CACHE MKL_MPI PROPERTY STRINGS AUTO openmpi intelmpi mpich)
84+
85+
string(TOLOWER "${MKL_MPI}" _mkl_mpi)
86+
if(_mkl_mpi STREQUAL "auto")
87+
if("${MPI_CXX_LIBRARY_VERSION_STRING}" MATCHES "Open MPI")
88+
set(_mkl_mpi openmpi)
89+
else()
90+
set(_mkl_mpi intelmpi)
91+
endif()
92+
elseif(_mkl_mpi STREQUAL "mpich")
93+
# MPICH uses Intel-MPI-compatible BLACS on Unix.
94+
set(_mkl_mpi intelmpi)
95+
elseif(NOT _mkl_mpi MATCHES "^(openmpi|intelmpi)$")
96+
message(FATAL_ERROR "MKL_MPI must be AUTO, openmpi, intelmpi, or mpich.")
3497
endif()
35-
find_library(MKL_SCALAPACK NAMES mkl_scalapack_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
36-
find_library(MKL_BLACS NAMES ${MKL_BLACS_LIB_NAME} HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
98+
99+
set(_mkl_blacs_name mkl_blacs_${_mkl_mpi}_lp64)
37100
endif()
38101

39-
include(FindPackageHandleStandardArgs)
40-
# handle the QUIETLY and REQUIRED arguments and set MKL_FOUND to TRUE
41-
# if all listed variables are TRUE
102+
# Re-run searches when a configuration axis or the selected MKL root changes.
103+
# This avoids reusing a library cached for another link mode, threading layer,
104+
# MPI ABI, or oneMKL installation.
105+
set(_mkl_search_signature
106+
"${_mkl_root_hints};${_mkl_link};${_mkl_interface_name};${_mkl_threading};${_mkl_blacs_name}"
107+
)
108+
if(DEFINED _MKL_SEARCH_SIGNATURE
109+
AND NOT "${_MKL_SEARCH_SIGNATURE}" STREQUAL "${_mkl_search_signature}")
110+
foreach(
111+
_mkl_cache_var IN
112+
ITEMS MKL_INCLUDE
113+
MKL_FFTW_INCLUDE
114+
MKL_INTERFACE_LIB
115+
MKL_THREAD
116+
MKL_CORE
117+
MKL_SCALAPACK
118+
MKL_BLACS)
119+
unset(${_mkl_cache_var} CACHE)
120+
endforeach()
121+
endif()
122+
set(_MKL_SEARCH_SIGNATURE "${_mkl_search_signature}"
123+
CACHE INTERNAL "Configuration used for the current oneMKL search")
42124

125+
set(_mkl_saved_suffixes "${CMAKE_FIND_LIBRARY_SUFFIXES}")
126+
if(_mkl_link STREQUAL "static")
127+
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
128+
elseif(_mkl_link STREQUAL "dynamic")
129+
set(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
130+
endif()
131+
132+
find_path(MKL_INCLUDE
133+
NAMES mkl.h
134+
HINTS ${_mkl_root_hints}
135+
PATH_SUFFIXES include)
136+
find_path(MKL_FFTW_INCLUDE
137+
NAMES fftw3.h
138+
HINTS ${_mkl_root_hints}
139+
PATH_SUFFIXES include/fftw)
140+
find_library(MKL_INTERFACE_LIB
141+
NAMES ${_mkl_interface_name}
142+
HINTS ${_mkl_root_hints}
143+
PATH_SUFFIXES lib/intel64 lib)
144+
find_library(MKL_THREAD
145+
NAMES mkl_${_mkl_threading}
146+
HINTS ${_mkl_root_hints}
147+
PATH_SUFFIXES lib/intel64 lib)
148+
find_library(MKL_CORE
149+
NAMES mkl_core
150+
HINTS ${_mkl_root_hints}
151+
PATH_SUFFIXES lib/intel64 lib)
43152
if(ENABLE_MPI)
44-
find_package_handle_standard_args(MKL DEFAULT_MSG MKL_INTERFACE_LIB MKL_THREAD MKL_CORE MKL_SCALAPACK MKL_BLACS MKL_INCLUDE)
45-
else()
46-
find_package_handle_standard_args(MKL MKL_INTERFACE_LIB MKL_THREAD MKL_CORE MKL_INCLUDE)
153+
find_library(MKL_SCALAPACK
154+
NAMES mkl_scalapack_lp64
155+
HINTS ${_mkl_root_hints}
156+
PATH_SUFFIXES lib/intel64 lib)
157+
find_library(MKL_BLACS
158+
NAMES ${_mkl_blacs_name}
159+
HINTS ${_mkl_root_hints}
160+
PATH_SUFFIXES lib/intel64 lib)
47161
endif()
162+
set(CMAKE_FIND_LIBRARY_SUFFIXES "${_mkl_saved_suffixes}")
163+
164+
set(_mkl_required_vars MKL_INCLUDE MKL_FFTW_INCLUDE MKL_INTERFACE_LIB MKL_THREAD MKL_CORE)
165+
if(ENABLE_MPI)
166+
list(APPEND _mkl_required_vars MKL_SCALAPACK MKL_BLACS)
167+
endif()
168+
find_package_handle_standard_args(MKL REQUIRED_VARS ${_mkl_required_vars})
48169

49170
if(MKL_FOUND)
50-
if(NOT TARGET MKL::INTERFACE_LIB)
51-
add_library(MKL::INTERFACE_LIB UNKNOWN IMPORTED)
52-
set_target_properties(MKL::INTERFACE_LIB PROPERTIES
53-
IMPORTED_LOCATION "${MKL_INTERFACE_LIB}"
54-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
55-
endif()
56-
if(NOT TARGET MKL::THREAD)
57-
add_library(MKL::THREAD UNKNOWN IMPORTED)
58-
set_target_properties(MKL::THREAD PROPERTIES
59-
IMPORTED_LOCATION "${MKL_THREAD}"
60-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
61-
endif()
62-
if(NOT TARGET MKL::CORE)
63-
add_library(MKL::CORE UNKNOWN IMPORTED)
64-
set_target_properties(MKL::CORE PROPERTIES
65-
IMPORTED_LOCATION "${MKL_CORE}"
66-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
171+
set(_mkl_libraries ${MKL_INTERFACE_LIB} ${MKL_THREAD} ${MKL_CORE})
172+
if(ENABLE_MPI)
173+
list(APPEND _mkl_libraries ${MKL_SCALAPACK} ${MKL_BLACS})
67174
endif()
68-
if(NOT TARGET MKL::MKL_SCALAPACK)
69-
add_library(MKL::MKL_SCALAPACK UNKNOWN IMPORTED)
70-
set_target_properties(MKL::MKL_SCALAPACK PROPERTIES
71-
IMPORTED_LOCATION "${MKL_SCALAPACK}"
72-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
175+
set(_mkl_any_static FALSE)
176+
set(_mkl_all_static TRUE)
177+
foreach(_mkl_library IN LISTS _mkl_libraries)
178+
if(_mkl_library MATCHES "\\.a$")
179+
set(_mkl_any_static TRUE)
180+
else()
181+
set(_mkl_all_static FALSE)
182+
endif()
183+
endforeach()
184+
if(_mkl_any_static AND NOT _mkl_all_static)
185+
message(
186+
FATAL_ERROR "The selected oneMKL libraries mix static and shared files. "
187+
"Choose a consistent MKL_LINK mode or library set.")
73188
endif()
74-
if(ENABLE_MPI AND NOT TARGET MKL::BLACS)
75-
add_library(MKL::BLACS UNKNOWN IMPORTED)
76-
set_target_properties(MKL::BLACS PROPERTIES
77-
IMPORTED_LOCATION "${MKL_BLACS}"
78-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
189+
if(_mkl_link STREQUAL "static" AND NOT _mkl_all_static)
190+
message(FATAL_ERROR "MKL_LINK=static did not select static oneMKL libraries.")
191+
elseif(_mkl_link STREQUAL "dynamic" AND _mkl_all_static)
192+
message(FATAL_ERROR "MKL_LINK=dynamic did not select shared oneMKL libraries.")
79193
endif()
80-
if(MKL_IOMP5 AND NOT TARGET MKL::IOMP5)
81-
add_library(MKL::IOMP5 UNKNOWN IMPORTED)
82-
set_target_properties(MKL::IOMP5 PROPERTIES
83-
IMPORTED_LOCATION "${MKL_IOMP5}")
84-
endif()
85-
add_library(MKL::MKL INTERFACE IMPORTED)
86-
if (ENABLE_MPI)
87-
set_property(TARGET MKL::MKL PROPERTY
88-
INTERFACE_LINK_LIBRARIES
89-
"-Wl,--start-group"
90-
MKL::INTERFACE_LIB MKL::THREAD MKL::CORE MKL::MKL_SCALAPACK MKL::BLACS
91-
"-Wl,--end-group"
92-
)
93-
else()
94-
set_property(TARGET MKL::MKL PROPERTY
95-
INTERFACE_LINK_LIBRARIES
96-
"-Wl,--start-group"
97-
MKL::INTERFACE_LIB MKL::THREAD MKL::CORE
98-
"-Wl,--end-group"
99-
)
100-
endif()
101-
if(TARGET MKL::IOMP5)
102-
set_property(TARGET MKL::MKL APPEND PROPERTY
103-
INTERFACE_LINK_LIBRARIES MKL::IOMP5)
104-
endif()
105-
endif()
106194

107-
if(ENABLE_MPI)
108-
mark_as_advanced(MKL_INCLUDE MKL_INTERFACE_LIB MKL_THREAD MKL_CORE MKL_SCALAPACK MKL_BLACS)
109-
else()
110-
mark_as_advanced(MKL_INCLUDE MKL_INTERFACE_LIB MKL_THREAD MKL_CORE)
111-
endif()
195+
find_package(Threads REQUIRED)
196+
set(_mkl_runtime Threads::Threads ${CMAKE_DL_LIBS})
197+
list(APPEND _mkl_runtime m)
198+
if(NOT _mkl_threading STREQUAL "sequential")
199+
find_package(OpenMP REQUIRED COMPONENTS CXX)
200+
list(APPEND _mkl_runtime OpenMP::OpenMP_CXX)
201+
endif()
112202

113-
endif() # MKL::MKL
203+
function(_mkl_link_group output)
204+
if(_mkl_all_static AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
205+
list(JOIN ARGN "," _mkl_archives)
206+
set(${output} "-Wl,--start-group,${_mkl_archives},--end-group" PARENT_SCOPE)
207+
else()
208+
set(${output} "${ARGN}" PARENT_SCOPE)
209+
endif()
210+
endfunction()
114211

212+
set(_mkl_base_archives ${MKL_INTERFACE_LIB} ${MKL_THREAD} ${MKL_CORE})
213+
_mkl_link_group(_mkl_base ${_mkl_base_archives})
115214

215+
add_library(abacus_mkl INTERFACE)
216+
add_library(abacus::mkl ALIAS abacus_mkl)
217+
target_include_directories(
218+
abacus_mkl
219+
INTERFACE
220+
"${MKL_INCLUDE}"
221+
"${MKL_FFTW_INCLUDE}")
222+
target_link_libraries(abacus_mkl INTERFACE ${_mkl_base} ${_mkl_runtime})
116223

117-
# In oneAPI 2022, MKL_SCALAPACK might not be linked properly
118-
if(NOT TARGET MKL::MKL_SCALAPACK)
119-
find_library(MKL_SCALAPACK NAMES mkl_scalapack_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
120-
message(STATUS "Found MKL_SCALAPACK: ${MKL_SCALAPACK}")
121-
if(MKL_SCALAPACK)
122-
# create an IMPORTED target that points to the discovered library file
123-
add_library(MKL::MKL_SCALAPACK UNKNOWN IMPORTED)
124-
set_target_properties(MKL::MKL_SCALAPACK PROPERTIES
125-
IMPORTED_LOCATION "${MKL_SCALAPACK}"
126-
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}"
127-
)
224+
if(ENABLE_MPI)
225+
add_library(abacus_mkl_scalapack INTERFACE)
226+
add_library(abacus::mkl_scalapack ALIAS abacus_mkl_scalapack)
227+
target_include_directories(
228+
abacus_mkl_scalapack
229+
INTERFACE
230+
"${MKL_INCLUDE}"
231+
"${MKL_FFTW_INCLUDE}")
232+
if(_mkl_all_static AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
233+
_mkl_link_group(_mkl_cluster ${MKL_SCALAPACK} ${MKL_BLACS} ${_mkl_base_archives})
234+
target_link_libraries(
235+
abacus_mkl_scalapack INTERFACE ${_mkl_cluster} MPI::MPI_CXX ${_mkl_runtime})
236+
else()
237+
target_link_libraries(
238+
abacus_mkl_scalapack INTERFACE
239+
${MKL_SCALAPACK} ${MKL_BLACS} abacus::mkl MPI::MPI_CXX)
240+
endif()
241+
set(MKL_LIBRARIES abacus::mkl_scalapack)
242+
else()
243+
set(MKL_LIBRARIES abacus::mkl)
128244
endif()
129245
endif()
246+
247+
mark_as_advanced(
248+
MKL_INCLUDE
249+
MKL_FFTW_INCLUDE
250+
MKL_INTERFACE_LIB
251+
MKL_THREAD
252+
MKL_CORE
253+
MKL_SCALAPACK
254+
MKL_BLACS)

0 commit comments

Comments
 (0)