多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 2.2 处理与平台相关的源代码 **NOTE**:*此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-02/recipe-02 中找到,包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。* 理想情况下,应该避免依赖于平台的源代码,但是有时我们没有选择,特别是当要求配置和编译不是自己编写的代码时。本示例中,将演示如何使用CMake根据操作系统编译源代码。 ## 准备工作 修改`hello-world.cpp`示例代码,将第1章第1节的例子进行修改: ```c++ #include <cstdlib> #include <iostream> #include <string> std::string say_hello() { #ifdef IS_WINDOWS return std::string("Hello from Windows!"); #elif IS_LINUX return std::string("Hello from Linux!"); #elif IS_MACOS return std::string("Hello from macOS!"); #else return std::string("Hello from an unknown system!"); #endif } int main() { std::cout << say_hello() << std::endl; return EXIT_SUCCESS; } ``` ## 具体实施 完成一个`CMakeLists.txt`实例,使我们能够基于目标操作系统有条件地编译源代码: 1. 首先,设置了CMake最低版本、项目名称和支持的语言: ```cmake cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(recipe-02 LANGUAGES CXX) ``` 2. 然后,定义可执行文件及其对应的源文件: ```cmake add_executable(hello-world hello-world.cpp) ``` 3. 通过定义以下目标编译定义,让预处理器知道系统名称: ```cmake if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(hello-world PUBLIC "IS_LINUX") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_compile_definitions(hello-world PUBLIC "IS_MACOS") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") target_compile_definitions(hello-world PUBLIC "IS_WINDOWS") endif() ``` 继续之前,先检查前面的表达式,并考虑在不同系统上有哪些行为。 4. 现在,准备测试它,并配置项目: ```shell $ mkdir -p build $ cd build $ cmake .. $ cmake --build . $ ./hello-world Hello from Linux! ``` Windows系统上,将看到来自Windows的Hello。其他操作系统将产生不同的输出。 ## 工作原理 `hello-world.cpp`示例中,有趣的部分是基于预处理器定义`IS_WINDOWS`、`IS_LINUX`或`IS_MACOS`的条件编译: ```cmake std::string say_hello() { #ifdef IS_WINDOWS return std::string("Hello from Windows!"); #elif IS_LINUX return std::string("Hello from Linux!"); #elif IS_MACOS return std::string("Hello from macOS!"); #else return std::string("Hello from an unknown system!"); #endif } ``` 这些定义在CMakeLists.txt中配置时定义,通过使用`target_compile_definition`在预处理阶段使用。可以不重复`if-endif`语句,以更紧凑的表达式实现,我们将在下一个示例中演示这种重构方式。也可以把`if-endif`语句加入到一个`if-else-else-endif`语句中。这个阶段,可以使用`add_definitions(-DIS_LINUX)`来设置定义(当然,可以根据平台调整定义),而不是使用`target_compile_definition`。使用`add_definitions`的缺点是,会修改编译整个项目的定义,而`target_compile_definitions`给我们机会,将定义限制于一个特定的目标,以及通过` PRIVATE|PUBLIC|INTERFACE `限定符,限制这些定义可见性。第1章的第8节,对这些限定符有详细的说明: - **PRIVATE**,编译定义将只应用于给定的目标,而不应用于相关的其他目标。 - **INTERFACE**,对给定目标的编译定义将只应用于使用它的目标。 - **PUBLIC**,编译定义将应用于给定的目标和使用它的所有其他目标。 **NOTE**:*将项目中的源代码与平台相关性最小化,可使移植更加容易。*