注意
本文最后更新于 2024-01-31,文中内容可能已过时。
一、导言
导言
本篇通过展示如何使用来自对应的CheckSourceCompiles.cmake标准模块的check__source_compiles函数,以评估给定编译器是否可以将预定义的代码编译成可执行文件。该命令可帮助确定:
- 编译器支持所需的特性。
- 链接器工作正常,并理解特定的标志。
- 可以使用find_package找到的包含目录和库。
我们将展示如何检测OpenMP 4.5标准的循环特性,以便在C++可执行文件中使用。使用一个C++源文件,来探测编译器是否支持这样的特性。CMake提供了一个附加命令try_compile来探究编译。
二、项目结构
1
2
| ├── CMakeLists.txt
└── task_loop.cpp
|
项目地址:
https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter5/04
CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OpenMP)
if(OpenMP_FOUND)
# this will get wiped unless you run cmake with --debug-trycompile
set(scratch_dir ${CMAKE_CURRENT_BINARY_DIR}/omp_try_compile)
try_compile(
omp_task_loop_test_1
${scratch_dir}
SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/task_loop.cpp
LINK_LIBRARIES
OpenMP::OpenMP_CXX
)
message(STATUS "Result of try_compile: ${omp_task_loop_test_1}")
include(CheckCXXSourceCompiles)
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/task_loop.cpp snippet)
set(CMAKE_REQUIRED_LIBRARIES OpenMP::OpenMP_CXX)
check_cxx_source_compiles("${snippet}" omp_task_loop_test_2)
unset(CMAKE_REQUIRED_LIBRARIES)
message(STATUS "Result of check_cxx_source_compiles: ${omp_task_loop_test_2}")
else()
message(STATUS "OpenMP not found: no test for taskloop is run")
endif()
|
方式一:
1
| set(scratch_dir ${CMAKE_CURRENT_BINARY_DIR}/omp_try_compile)
|
如果找到OpenMP
,再检查所需的特性是否可用。为此,设置了一个临时目录,try_compile
将在这个目录下来生成中间文件。我们把它放在前面步骤中引入的if
语句中。如果我们构建时使用如下命令,则会在omp_try_compile
文件夹中产生中间文件。
1
| cmake .. --debug-trycompile
|
生成可执行文件cmTC_e8239
1
2
3
4
5
6
7
8
9
| try_compile(
omp_task_loop_test_1
${scratch_dir}
SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/task_loop.cpp
LINK_LIBRARIES
OpenMP::OpenMP_CXX
)
message(STATUS "Result of try_compile: ${omp_task_loop_test_1}")
|
调用try_compile
生成一个小项目,以尝试编译源文件task_loop.cpp
。编译成功或失败的状态,将保存到omp_task_loop_test_1
变量中。需要为这个示例编译设置适当的编译器标志、包括目录和链接库。因为使用导入的目标OpenMP::OpenMP_CXX
,所以只需将LINK_LIBRARIES
选项设置为OpenMP::OpenMP_CXX
即可。如果编译成功,则任务循环特性可用,我们打印一条消息。
方式二:
1
| include(CheckCXXSourceCompiles)
|
要使用check_cxx_source_compiles
函数,需要包含CheckCXXSourceCompiles.cmake
模块文件。其他语言也有类似的模块文件,C
(CheckCSourceCompiles.cmake
)和Fortran
(CheckFortranSourceCompiles.cmake
)。
1
| file(READ ${CMAKE_CURRENT_SOURCE_DIR}/task_loop.cpp snippet)
|
复制源文件的内容,通过file(READ ...)
命令读取内容到一个变量中,试图编译和连接这个变量。
1
| set(CMAKE_REQUIRED_LIBRARIES OpenMP::OpenMP_CXX)
|
设置了CMAKE_REQUIRED_LIBRARIES
。对于下一步正确调用编译器是必需的。注意使用导入的OpenMP::OpenMP_CXX
目标,它还将设置正确的编译器标志和包含目录。
1
| check_cxx_source_compiles("${snippet}" omp_task_loop_test_2)
|
使用代码片段作为参数,调用check_cxx_source_compiles
函数。检查结果将保存到omp_task_loop_test_2
变量中。
1
2
| unset(CMAKE_REQUIRED_LIBRARIES)
message(STATUS "Result of check_cxx_source_compiles: ${omp_task_loop_test_2}"
|
调用check_cxx_source_compiles
并向用户打印消息之前,取消变量的设置。
task_loop.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| #include <cmath>
#include <iostream>
void LongRunningTask() {
double number = 5.0;
double result = std::pow(number, 2);
std::cout << "长时间运行的任务结果:" << result << std::endl;
}
void LoopBody(int i, int j) {
double calculation_result = std::sin(i) * std::cos(j);
std::cout << "在循环体中计算结果:" << calculation_result << std::endl;
}
void ParallelWork() {
int i, j;
#pragma omp taskgroup
{
#pragma omp task
LongRunningTask();
#pragma omp taskloop private(j) grainsize(500) nogroup
for (i = 0; i < 10000; i++) {
for (j = 0; j < i; j++) {
LoopBody(i, j);
}
}
}
}
int main() {
ParallelWork();
return 0;
}
|
结果
1
2
3
4
5
6
7
8
9
10
| $ mkdir -p build
$ cd build
$ cmake ..
-- ...
-- Found OpenMP_CXX: -fopenmp (found version "4.5")
-- Found OpenMP: TRUE (found version "4.5")
-- Result of try_compile: TRUE
-- Performing Test omp_taskloop_test_2
-- Performing Test omp_taskloop_test_2 - Success
-- Result of check_cxx_source_compiles: 1
|