如何在 CMake 中为 Intel 编译器正确查找 BLAS 和 OpenMP?
可以继续使用 BLAS::BLAS 和 LAPACK::LAPACK,通过 BLA_VENDOR 指定 MKL。使用 Intel 编译器不需要改成 MKL::MKL。
OpenMP::OpenMP_Fortran 缺失需要单独排查。常见原因包括尚未启用 Fortran、切换编译器后沿用了旧缓存、oneAPI 环境加载不完整,或 CMake 对当前 Intel 编译器的检测支持不足。C 的 OpenMP 检测成功,也不能说明 Fortran 环境正常。
使用标准查找模块选择 MKL
CMake 的 FindBLAS 和 FindLAPACK 已支持 MKL。对于使用 32 位整数接口的 64 位程序,可以这样配置:
cmake -S . -B build-intel \
-DCMAKE_C_COMPILER=icx \
-DCMAKE_Fortran_COMPILER=ifx \
-DBLA_VENDOR=Intel10_64lp \
-DBLA_SIZEOF_INTEGER=4
Intel10_64lp 是 CMake 定义的供应商选项名称,适用于 MKL v10 及后续版本,不要求安装 MKL 10。它选择 LP64、多线程版本;需要顺序版本时,可以改用 Intel10_64lp_seq。参见 FindBLAS 的 MKL 选项说明。
项目中的链接代码可以保持统一。例如,混合 C、Fortran 的可执行目标可以这样写,源文件名按实际项目替换:
cmake_minimum_required(VERSION 3.22)
project(MySolver LANGUAGES C Fortran)
set(BLA_SIZEOF_INTEGER 4 CACHE STRING
"Size in bytes of BLAS/LAPACK integers")
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
find_package(OpenMP REQUIRED COMPONENTS C Fortran)
add_executable(solver main.c solver.f90)
target_link_libraries(solver PRIVATE
LAPACK::LAPACK
BLAS::BLAS
OpenMP::OpenMP_C
OpenMP::OpenMP_Fortran
)
将 BLA_VENDOR 放在配置命令或 CMake Preset 中即可,业务目标无需判断编译器品牌。GNU 构建也可以使用 MKL,Intel 构建也可以选择其他库。
示例要求 CMake 最低为 3.22,因为 BLA_SIZEOF_INTEGER 从该版本开始提供。不过,3.22 并不能保证解决所有后续 Intel 编译器的兼容问题。LAPACK::LAPACK 是标准查找模块提供的导入目标。参见 FindBLAS 和 FindLAPACK。
BLA_SIZEOF_INTEGER=4 只控制所查找库的整数接口,不会修改 Fortran 变量的 kind,也不会改变编译器的默认整数大小。调用代码的整数类型必须与库接口一致。
先加载完整的 oneAPI 环境
即使已经安装 MKL,启动 CMake 的进程也可能找不到它的全部依赖。
在 Linux 上,传统 oneAPI 安装布局通常可以这样初始化:
source /opt/intel/oneapi/setvars.sh
路径以实际安装为准;其他安装布局应使用对应的环境初始化脚本。初始化后,在同一个终端中运行 CMake。
检查是否只加载了 MKL 组件环境。CMake 文档提醒,仅加载 MKL 环境可能无法提供 iomp5,还需要编译器组件环境或其他有效的运行库路径。这可能同时影响多线程 MKL 和 OpenMP 的链接检查。参见 CMake 的 Intel MKL 环境说明。
Windows 上应从已初始化 Intel 编译器环境的命令提示符运行 CMake。如果通过 IDE 配置,也要确认 IDE 进程继承了相同环境。
确认 Fortran 在查找 OpenMP 前已经启用
标准目标的完整名称是:
OpenMP::OpenMP_C
OpenMP::OpenMP_Fortran
OpenMP_Fortran_FOUND 是检测结果变量,不是链接目标。
应在 find_package(OpenMP ...) 前启用 Fortran:
project(MySolver LANGUAGES C Fortran)
find_package(OpenMP REQUIRED COMPONENTS C Fortran)
如果项目最初只启用了 C,之后才调用 enable_language(Fortran),此前的 OpenMP 查找不会自动补做 Fortran 检测。
不指定 COMPONENTS 时,FindOpenMP 会检测当时已启用的语言。显式写出 C Fortran 可以明确所需组件,也能更早发现缺失项。参见 FindOpenMP 文档。
切换编译器时使用新的构建目录
从 GNU 切换到 Intel 时,建议使用全新的构建目录,例如前面的 build-intel。如果只修改 CC、FC 就重新配置旧目录,CMake 可能继续使用缓存中的编译器路径和依赖检测结果。
还要检查 CMake 实际识别到的编译器。可以在 project() 后临时加入:
message(STATUS
"C compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})")
message(STATUS
"Fortran compiler: ${CMAKE_Fortran_COMPILER} "
"(${CMAKE_Fortran_COMPILER_ID})")
Intel Classic 和基于 LLVM 的 Intel 编译器使用不同的 ID,分别为 Intel 和 IntelLLVM。如果项目或自定义查找模块只判断:
if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
这个条件不会匹配 IntelLLVM。参见 CMake 编译器 ID 说明。
根据探测日志定位失败环节
FindOpenMP 会分别测试各语言的编译和链接。Fortran 检测还涉及 omp_lib 模块或 omp_lib.h,因此可能出现 C 检测成功、Fortran 检测失败的情况。
可以使用新的诊断构建目录,保留探测文件并输出查找路径:
cmake -S . -B build-intel-debug \
-DCMAKE_C_COMPILER=icx \
-DCMAKE_Fortran_COMPILER=ifx \
-DBLA_VENDOR=Intel10_64lp \
--debug-find \
--debug-trycompile
这两个选项分别用于查看查找过程和保留测试编译目录,具体说明见 CMake 命令行文档。
使用 CMake 3.26 及后续版本时,可以检查:
build-intel-debug/CMakeFiles/CMakeConfigureLog.yaml
较旧版本通常查看 CMakeFiles/CMakeError.log 和 CMakeFiles/CMakeOutput.log。配置日志的用途及位置见 cmake-configure-log。
排查时,重点查看失败测试使用的命令,以及第一条有实际意义的错误:
| 错误现象 | 优先检查 |
|---|---|
| 不认识 OpenMP 编译选项 | 实际编译器、CMake 版本、项目中的全局编译参数 |
找不到或无法读取 omp_lib | Fortran 安装、模块搜索路径、是否混入其他编译器版本的模块 |
找不到 iomp5 等库 | 编译器组件环境、库搜索路径、安装组件是否完整 |
| 出现 OpenMP 未定义符号 | 实际链接命令是否包含所需选项和运行库 |
| BLAS 库已找到,但测试链接失败 | MKL 的依赖库、整数接口、架构及链接参数 |
当前 CMake 的 FindOpenMP 对 Intel 和 IntelLLVM 采用不同的选项处理方式。如果使用较旧的 CMake 搭配较新的 ifx,可以先换用较新的 CMake 稳定版,在全新构建目录中复查。缺少具体版本和日志时,无法确定是哪项兼容问题。相关检测逻辑可见 FindOpenMP 源码。
用最小 Fortran 程序验证编译器环境
也可以绕过 CMake,将以下程序保存为 omp_check.f90:
program omp_check
use omp_lib
implicit none
!$omp parallel
print *, "thread", omp_get_thread_num()
!$omp end parallel
end program omp_check
在已初始化环境的 Linux 终端中,用 ifx 编译并运行:
ifx -fiopenmp omp_check.f90 -o omp_check
OMP_NUM_THREADS=2 ./omp_check
-fiopenmp 是 ifx 的 OpenMP 选项,Windows 对应 /Qiopenmp。编译和链接阶段都需要正确启用 OpenMP。参见 Intel Fortran 选项说明。
如果这个程序也无法编译或链接,先根据报错解决模块、运行库或环境问题。如果程序能正常构建并运行,但 CMake 检测失败,就比较两者使用的编译器路径和命令参数,再检查缓存、工具链文件及 CMake 版本。
不要将 OpenMP_Fortran_FOUND 强制设为 TRUE,也不要用 OpenMP::OpenMP_C 代替 Fortran 目标。这些做法会跳过所需检查,Fortran 源文件仍可能缺少正确的编译选项。
备注:内容仅供参考。
