企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 2.4 检测处理器体系结构 **NOTE**:*此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-02/recipe-04 中找到,包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。* 19世纪70年代,出现的64位整数运算和本世纪初出现的用于个人计算机的64位寻址,扩大了内存寻址范围,开发商投入了大量资源来移植为32位体系结构硬编码,以支持64位寻址。许多博客文章,如 https://www.viva64.com/en/a/0004/ ,致力于讨论将`C++`代码移植到64位平台中的典型问题和解决方案。虽然,避免显式硬编码的方式非常明智,但需要在使用CMake配置的代码中适应硬编码限制。本示例中,我们会来讨论检测主机处理器体系结构的选项。 ## 准备工作 我们以下面的`arch-dependent.cpp`代码为例: ```c++ #include <cstdlib> #include <iostream> #include <string> #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) std::string say_hello() { std::string arch_info(TOSTRING(ARCHITECTURE)); arch_info += std::string(" architecture. "); #ifdef IS_32_BIT_ARCH return arch_info + std::string("Compiled on a 32 bit host processor."); #elif IS_64_BIT_ARCH return arch_info + std::string("Compiled on a 64 bit host processor."); #else return arch_info + std::string("Neither 32 nor 64 bit, puzzling ..."); #endif } int main() { std::cout << say_hello() << std::endl; return EXIT_SUCCESS; } ``` ## 具体实施 `CMakeLists.txt`文件中,我们需要以下内容: 1. 首先,定义可执行文件及其源文件依赖关系: ```cmake cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(recipe-04 LANGUAGES CXX) add_executable(arch-dependent arch-dependent.cpp) ``` 2. 检查空指针类型的大小。CMake的`CMAKE_SIZEOF_VOID_P`变量会告诉我们CPU是32位还是64位。我们通过状态消息让用户知道检测到的大小,并设置预处理器定义: ```cmake if(CMAKE_SIZEOF_VOID_P EQUAL 8) target_compile_definitions(arch-dependent PUBLIC "IS_64_BIT_ARCH") message(STATUS "Target is 64 bits") else() target_compile_definitions(arch-dependent PUBLIC "IS_32_BIT_ARCH") message(STATUS "Target is 32 bits") endif() ``` 3. 通过定义以下目标编译定义,让预处理器了解主机处理器架构,同时在配置过程中打印状态消息: ```cmake if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i386") message(STATUS "i386 architecture detected") elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i686") message(STATUS "i686 architecture detected") elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") message(STATUS "x86_64 architecture detected") else() message(STATUS "host processor architecture is unknown") endif() target_compile_definitions(arch-dependent PUBLIC "ARCHITECTURE=${CMAKE_HOST_SYSTEM_PROCESSOR}" ) ``` 4. 配置项目,并注意状态消息(打印出的信息可能会发生变化): ```shell $ mkdir -p build $ cd build $ cmake .. ... -- Target is 64 bits -- x86_64 architecture detected ... ``` 5. 最后,构建并执行代码(实际输出将取决于处理器架构): ```shell $ cmake --build . $ ./arch-dependent x86_64 architecture. Compiled on a 64 bit host processor. ``` ## 工作原理 CMake定义了`CMAKE_HOST_SYSTEM_PROCESSOR`变量,以包含当前运行的处理器的名称。可以设置为“i386”、“i686”、“x86_64”、“AMD64”等等,当然,这取决于当前的CPU。`CMAKE_SIZEOF_VOID_P`为void指针的大小。我们可以在CMake配置时进行查询,以便修改目标或目标编译定义。可以基于检测到的主机处理器体系结构,使用预处理器定义,确定需要编译的分支源代码。正如在前面的示例中所讨论的,编写新代码时应该避免这种依赖,但在处理遗留代码或交叉编译时,这种依赖是有用的,交叉编译会在第13章进行讨论。 **NOTE**:*使用`CMAKE_SIZEOF_VOID_P`是检查当前CPU是否具有32位或64位架构的唯一“真正”可移植的方法。* ## 更多信息 除了`CMAKE_HOST_SYSTEM_PROCESSOR`, CMake还定义了`CMAKE_SYSTEM_PROCESSOR`变量。前者包含当前运行的CPU在CMake的名称,而后者将包含当前正在为其构建的CPU的名称。这是一个细微的差别,在交叉编译时起着非常重要的作用。我们将在第13章,看到更多关于交叉编译的内容。另一种让CMake检测主机处理器体系结构,是使用`C`或`C++中`定义的符号,结合CMake的`try_run`函数,尝试构建执行的源代码(见第5.8节)分支的预处理符号。这将返回已定义错误码,这些错误可以在CMake端捕获(此策略的灵感来自 https://github.com/axr/cmake/blob/master/targetarch.cmake ): ```c++ #if defined(__i386) || defined(__i386__) || defined(_M_IX86) #error cmake_arch i386 #elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) #error cmake_arch x86_64 #endif ``` 这种策略也是检测目标处理器体系结构的推荐策略,因为CMake似乎没有提供可移植的内在解决方案。另一种选择,将只使用CMake,完全不使用预处理器,代价是为每种情况设置不同的源文件,然后使用`target_source `命令将其设置为可执行目标`arch-dependent`依赖的源文件: ```cmake add_executable(arch-dependent "") if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i386") message(STATUS "i386 architecture detected") target_sources(arch-dependent PRIVATE arch-dependent-i386.cpp ) elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i686") message(STATUS "i686 architecture detected") target_sources(arch-dependent PRIVATE arch-dependent-i686.cpp ) elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") message(STATUS "x86_64 architecture detected") target_sources(arch-dependent PRIVATE arch-dependent-x86_64.cpp ) else() message(STATUS "host processor architecture is unknown") endif() ``` 这种方法,显然需要对现有项目进行更多的工作,因为源文件需要分离。此外,不同源文件之间的代码复制肯定也会成为问题。