这个方法与前面的方法类似:我们将使用 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后缀。