CMake 系列(十一):处理与编译器相关的源代码

这个方法与前面的方法类似:我们将使用 CMake 来编译依赖于环境的条件源代码,本例中该依赖是编译器。为了可移植性,我们应尽量避免编写新代码,但遇到有依赖的情况也要去解决,特别是在使用历史代码或处理编译器相关工具(如 sanitizers)时。从这一章和前一章的示例中,我们已经掌握了实现这一目标的所有方法。尽管如此,讨论与编译器相关的源代码处理问题还是很有用的,这样我们就有机会从另一个角度了解 CMake。

本示例将从 C++ 示例开始,稍后再演示一个 Fortran 示例,并尝试重构和简化 CMake 代码。

注意:本示例包含 C++ 和 Fortran 两个示例,在 CMake 3.5 或更高版本中有效,并已在 GNU/Linux、macOS 和 Windows 上测试通过。

具体步骤

先来看 hello-world.cpp 的源代码:

#include <cstdlib>
#include <iostream>
#include <string>

std::string say_hello() {
#ifdef IS_INTEL_CXX_COMPILER
  // only compiled when Intel compiler is selected
  // such compiler will not compile the other branches
  return std::string("Hello Intel compiler!");
#elif IS_GNU_CXX_COMPILER
  // only compiled when GNU compiler is selected
  // such compiler will not compile the other branches
  return std::string("Hello GNU compiler!");
#elif IS_PGI_CXX_COMPILER
  // etc.
  return std::string("Hello PGI compiler!");
#elif IS_XL_CXX_COMPILER
  return std::string("Hello XL compiler!");
#else
  return std::string("Hello unknown compiler - have we met before?");
#endif
}

int main() {
  std::cout << say_hello() << std::endl;
  std::cout << "compiler name is " COMPILER_NAME << std::endl;
  return EXIT_SUCCESS;
}

Fortran 示例(hello-world.F90):

program hello

  implicit none
#ifdef IS_Intel_FORTRAN_COMPILER
  print *, 'Hello Intel compiler!'
#elif IS_GNU_FORTRAN_COMPILER
  print *, 'Hello GNU compiler!'
#elif IS_PGI_FORTRAN_COMPILER
  print *, 'Hello PGI compiler!'
#elif IS_XL_FORTRAN_COMPILER
  print *, 'Hello XL compiler!'
#else
  print *, 'Hello unknown compiler - have we met before?'
#endif

end program

现在我们已经可以预测结果了,配置、构建并运行:

$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build .
$ ./hello-world

输出如下:

Hello GNU compiler!

使用不同的编译器,此示例代码将打印不同的问候语。

工作原理

CMakeLists.txt 会在配置时生成预处理定义,并传递给预处理器。前一个示例的 CMakeLists.txt 中的 if 语句显得有些重复,我们不喜欢重复的语句。能更简洁地表达吗?当然可以!为此,让我们再来看看 Fortran 示例:它包含非常紧凑的表达式,我们使用 CMAKE_Fortran_COMPILER_ID 变量,通过 target_compile_definitions 构造预处理器定义。为了适应这种情况,必须将 “Intel” 从 IS_INTEL_CXX_COMPILER 更改为 IS_Intel_FORTRAN_COMPILER。通过使用相应的 CMAKE_C_COMPILER_ID 和 CMAKE_CXX_COMPILER_ID 变量,我们可以在 C 或 C++ 中实现相同的效果。但请注意,CMAKE_<LANG>_COMPILER_ID 不能保证对所有编译器或语言都有定义。

注意:需要预处理的 Fortran 代码应使用 .F90 后缀,不需要预处理的代码使用 .f90 后缀。

系列导航


   转载规则


《CMake 系列(十一):处理与编译器相关的源代码》 由 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
CMake 系列(十二):检测 Python 解释器 CMake 系列(十二):检测 Python 解释器
Python 是一种非常流行的语言。许多项目使用 Python 编写的工具来打包主程序和库,或者在配置或构建过程中使用 Python 脚本。在这些情况下,需要确保运行时对 Python 解释器的依赖也得到满足。本示例将展示如何检测和使用 P
2022-05-31
下一篇 
CMake 系列(十):处理与平台相关的源代码 CMake 系列(十):处理与平台相关的源代码
理想情况下,应该避免依赖于平台的源代码,但是有时我们没有选择,特别是当要求配置和编译不是自己编写的代码时。本示例中,将演示如何使用 CMake 根据操作系统编译源代码。 提示:本示例包含一个 C++ 示例,在 CMake 3.5 或更高版
2022-05-17
  目录