CMake 笔记 | [26] 探究编译和编译命令

注意
本文最后更新于 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
Buy me a coffee~
支付宝
微信
0%