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代码为例:

#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_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位。我们通过状态消息让用户知道检测到的大小,并设置预处理器定义:

    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. 通过定义以下目标编译定义,让预处理器了解主机处理器架构,同时在配置过程中打印状态消息:

    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. 配置项目,并注意状态消息(打印出的信息可能会发生变化):

    $ mkdir -p build
    $ cd build
    $ cmake ..
    
    ...
    -- Target is 64 bits
    -- x86_64 architecture detected
    ...
  5. 最后,构建并执行代码(实际输出将取决于处理器架构):

    $ 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检测主机处理器体系结构,是使用CC++中定义的符号,结合CMake的try_run函数,尝试构建执行的源代码(见第5.8节)分支的预处理符号。这将返回已定义错误码,这些错误可以在CMake端捕获(此策略的灵感来自 https://github.com/axr/cmake/blob/master/targetarch.cmake ):

#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依赖的源文件:

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()

这种方法,显然需要对现有项目进行更多的工作,因为源文件需要分离。此外,不同源文件之间的代码复制肯定也会成为问题。

Last updated