2.3 处理与编译器相关的源代码
NOTE : 此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-02/recipe-03 中找到,包含一个 C++和 Fortran 示例。该示例在 CMake 3.5 版(或更高版本) 中是有效的,并且已经在 GNU/Linux、macOS 和 Windows 上进行过测试。
这个方法与前面的方法类似,我们将使用 CMake 来编译依赖于环境的条件源代码:本例将依赖于编译器。为了可移植性,我们尽量避免去编写新代码,但遇到有依赖的情况我们也要去解决,特别是当使用历史代码或处理编译器依赖工具,如 sanitizers 。从这一章和前一章的示例中,我们已经掌握了实现这一目标的所有方法。尽管如此,讨论与编译器相关的源代码的处理问题还是很有用的,这样我们将有机会从另一方面了解 CMake。
准备工作
本示例中,我们将从 C++
中的一个示例开始,稍后我们将演示一个 Fortran
示例,并尝试重构和简化 CMake 代码。
看一下 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
具体实施
我们将从 C++
的例子开始,然后再看 Fortran
的例子:
CMakeLists.txt
文件中,定义了 CMake 最低版本、项目名称和支持的语言:cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(recipe-03 LANGUAGES CXX)
- 然后,定义可执行目标及其对应的源文件:
add_executable(hello-world hello-world.cpp)
- 通过定义以下目标编译定义,让预处理器了解编译器的名称和供应商:
target_compile_definitions(hello-world PUBLIC "COMPILER_NAME=\"${CMAKE_CXX_COMPILER_ID}\"") if(CMAKE_CXX_COMPILER_ID MATCHES Intel) target_compile_definitions(hello-world PUBLIC "IS_INTEL_CXX_COMPILER") endif() if(CMAKE_CXX_COMPILER_ID MATCHES GNU) target_compile_definitions(hello-world PUBLIC "IS_GNU_CXX_COMPILER") endif() if(CMAKE_CXX_COMPILER_ID MATCHES PGI) target_compile_definitions(hello-world PUBLIC "IS_PGI_CXX_COMPILER") endif() if(CMAKE_CXX_COMPILER_ID MATCHES XL) target_compile_definitions(hello-world PUBLIC "IS_XL_CXX_COMPILER") endif()
现在我们已经可以预测结果了:
$ mkdir -p build $ cd build $ cmake .. $ cmake --build . $ ./hello-world Hello GNU compiler!
使用不同的编译器,此示例代码将打印不同的问候语。
前一个示例的 CMakeLists.txt
文件中的 if
语句似乎是重复的,我们不喜欢重复的语句。能更简洁地表达吗?当然可以!为此,让我们再来看看 Fortran
示例。
Fortran
例子的 CMakeLists.txt
文件中,我们需要做以下工作:
- 需要使
Fortran
语言:project(recipe-03 LANGUAGES Fortran)
- 然后,定义可执行文件及其对应的源文件。在本例中,使用大写
.F90
后缀:add_executable(hello-world hello-world.F90)
- 我们通过定义下面的目标编译定义,让预处理器非常清楚地了解编译器:
target_compile_definitions(hello-world PUBLIC "IS_${CMAKE_Fortran_COMPILER_ID}_FORTRAN_COMPILER" )
其余行为与 C++
示例相同。
工作原理
CMakeLists.txt
会在配置时,进行预处理定义,并传递给预处理器。 Fortran
示例包含非常紧凑的表达式,我们使用 CMAKE_Fortran_COMPILER_ID
变量,通过 target_compile_definition
使用构造预处理器进行预处理定义。为了适应这种情况,我们必须将"Intel"从 IS_INTEL_CXX_COMPILER
更改为 IS_Intel_FORTRAN_COMPILER
。通过使用相应的 CMAKE_C_COMPILER_ID
和 CMAKE_CXX_COMPILER_ID
变量,我们可以在 C
或 C++
中实现相同的效果。但是,请注意, CMAKE_<LANG>_COMPILER_ID
不能保证为所有编译器或语言都定义。
NOTE : 对于应该预处理的 Fortran
代码使用 .F90
后缀,对于不需要预处理的代码使用 .f90
后缀。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论