【问题标题】:How to detect target architecture using CMake?如何使用 CMake 检测目标架构?
【发布时间】:2012-08-10 06:11:54
【问题描述】:

我已经进行了大量研究,但无法找到答案……如何使用 CMake 可靠地找到我正在编译的目标架构?基本上相当于qmake中的QMAKE_TARGET.arch。

大多数消息来源似乎建议使用 CMAKE_SYSTEM_PROCESSOR,但这是一个糟糕的解决方案,因为例如,无论您是为 i386、x86_64、ppc 还是 ppc64 编译,它都会在 OS X 上返回 i386。

同样,CMAKE_SIZEOF_VOID_P 给出了系统的指针大小,而不是目标。

我知道有 CMAKE_OSX_ARCHITECTURES,但如果未设置,它可能为空,在这种情况下,它似乎默认为系统能够执行的任何操作。那么如何找到目标架构信息呢?

特别是对于 OS X,我如何区分 32、64 和 Intel Universal?

【问题讨论】:

  • CMAKE_SIZEOF_VOID_P 为我提供了不同的大小,具体取决于我是使用 -G "Visual Studio 11 Win64" 还是仅使用 -G "Visual Studio 11" 生成的——我用它来确定目标。这是否与您的“CMAKE_SIZEOF_VOID_P 给出系统的指针大小,而不是目标”的断言相矛盾?我只在 Visual Studio 上测试过这个...
  • 根据文档,CMAKE_SYSTEM_PROCESSOR 返回您正在编译的 CPU 的架构,而 CMAKE_HOST_SYSTEM_PROCESSOR 返回运行 CMake 的 CPU 架构。因此,即使您正在交叉编译,使用CMAKE_SYSTEM_PROCESSOR 也应该是安全的。你发现不是这样吗?但它确实返回不一致的结果(例如 Windows 上的“AMD64”,Linux 上的“x86_64”)。

标签: architecture cmake build-process qmake


【解决方案1】:

所以我为我的问题设计了一个相当有创意的解决方案……看来 CMake 没有任何功能来检测目标架构。

现在,我们知道我们可以在 C 中轻松做到这一点,因为 __i386__、__x86_64__ 等符号将根据您的环境进行定义。幸运的是,CMake 有一个 try_run 函数,可以在配置阶段编译和运行任意 C 源代码文件。

然后我们可以编写一个小程序,它使用一堆 ifdef 并将架构名称作为字符串写入控制台。唯一的问题是,这仅在主机和目标系统相同时才有效……它在交叉编译期间无法工作,因为虽然您可以编译二进制文件,但您无法运行它来查看其输出。

这就是事情变得有趣的地方。我们可以通过故意编写一个损坏的 C 程序来利用 C 预处理器来获取必要的信息……我们使用基于 ifdefs 将架构名称写入控制台的原始概念,但我们不会这样做,而是简单地放置一个 #错误预处理器指令代替 printf 调用。

当 CMake 的 try_run 函数编译 C 文件时,编译总是会失败,但是我们在 #error 指令中放置的任何消息都会显示在编译器的错误输出中,try_run 会返回给我们。

因此,我们所要做的就是使用一些 CMake 字符串命令从编译器的错误输出中解析架构名称,并且我们可以检索目标架构......即使在交叉编译时也是如此。

代码的 OS X 特定部分主要使用 CMAKE_OSX_ARCHITECTURES 来确定目标架构,但在未指定的情况下,它将使用与其他系统相同的代码并正确返回 x86_64(对于现代系统,这是编译器的默认值) 或 i386(适用于旧版 OS X 系统,例如 Leopard)。

我已经使用 Visual Studio 9 和 10 生成器(x86、x86_64、ia64)、Xcode、NMake、MSYS Makefile 和 Unix Makefile 在 Windows、OS X 和 Linux 上进行了测试和验证。每次都返回正确的结果。

注意:如果您故意将 -m32 或 -m64 传递给编译器,或其他可能影响目标架构的标志(有没有办法传递所有环境设置到 try_run?);这不是我测试过的东西。只要您使用生成器的默认设置,并且所有目标都针对相同的架构进行编译,您应该没问题。

我的解决方案的完整源代码可以在 GitHub 上找到:https://github.com/petroules/solar-cmake/blob/master/TargetArch.cmake

【讨论】:

  • 变量 CMAKE_HOST_SYSTEM_PROCESSOR 与此有何关系?不一样吗?
  • 这是 HOST 系统的处理器类型,即您用于构建的系统。这可能并不总是与目标相同,即您为其构建软件的系统。例如,当您编写 iPhone 应用程序时,您的 HOST 系统是 OS X,而您的 TARGET 是 iOS。如果您正在编写 Mac 应用程序,则主机和目标都将是 OS X。CMAKE_HOST_SYSTEM_PROCESSOR 通常完全没用 - 例如,它在 64 位 OS X 系统上返回“i386”,而在其他系统上可能返回其他不太有用的值也是。
  • CMAKE_SYSTEM_PROCESSOR 不能用于此目的吗?
  • 请原谅我的无知...为什么 Cmake 不按照您的描述进行操作并为我们提供变量?没有配置的生成有什么用?你有没有 set(CMAKE_VERBOSE_MAKEFILE on) 并查看过他们的标志在 每个 平台上有多糟糕,从 i686 和 x86_64 到 AMR 和 MIPS?难怪他们默认隐藏输出...
  • 很棒的帖子,感谢分享。我正在调整您的方法,但直接使用 CMake 的 execute_process 函数使用预处理器。 ${CMAKE_C_COMPILER} -E -P platformtest.c 与我的交叉编译器 ${CMAKE_C_COMPILER}。这具有较少噪音的输出,使用 STDOUT,可以区分成功和失败,不需要任何解析,不依赖于编译器如何显示错误。见gist.github.com/webmaster128/e08067641df1dd784eb195282fd0912f
【解决方案2】:

对于主机和目标系统相同的情况,我有一个解决方案。

首先您需要调用“uname -m”来获取“机器硬件名称”。之后,您需要切断尾随的“回车”以将实际值返回到提供的变量中。

EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE )

现在您可以打印出变量 ${ARCHITECTURE}:

message( STATUS "Architecture: ${ARCHITECTURE}" )

或者做一些规范化来映射,例如“x86_64”、“amd64”、...到例如“64 位”。 32 位也是如此。 有了这个,你可以执行架构依赖编译,如:

if( ${ARCHITECTURE} STREQUAL "64Bit" )
    set( BLA_LIBRARY "/opt/lib/libBla.so" )
else()
    set( BLA_LIBRARY "/opt/lib32/libBla.so" )
endif()

【讨论】:

  • CMAKE_SYSTEM_PROCESSOR 包含 uname -s 或在 Windows 上的环境变量 PROCESSOR_ARCHITECTURE 的输出。
  • 错了。 uname -s 是CMAKE_SYSTEM_NAME
  • 除上述之外:CMAKE_SYSTEM_PROCESSOR 与目标系统上的 uname -p 相同。
  • @user2746401 windows上的环境变量PROCESSOR_ARCHITECTURE取决于运行的控制台进程。更重要的是,cmake 本身可以是 32 位或 64 位进程。更重要的是,一些 64 位编译器可以同时生成 32 位和 64 位模块,这又取决于命令行键。所以没有办法可移植地检测架构。
  • tr 可以通过使用 OUTPUT_STRIP_TRAILING_WHITESPACE 来避免:stackoverflow.com/a/42452019/3196753
【解决方案3】:

Android ${ANDROID_ABI}

${ANDROID_ABI} 变量是在 Android 中采用的方式,它采用 arm64-v8a、x86_64 等值。

在官方NDK库示例中使用:https://github.com/googlesamples/android-ndk/blob/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/app/src/main/cpp/CMakeLists.txt#L25

我在NDK: How to include Prebuilt Shared Library Regardless of Architecture 上对该示例进行了进一步评论

【讨论】:

    【解决方案4】:

    如果您的构建过程涉及超过 1 个目标,那么最好让 CMake 知道它正在构建的 ARCH/工具链是什么。 您可以按照instructions 进行 CMake 交叉编译,它鼓励您创建工具链 CMake 文件,让您选择正在使用的工具链/编译器。

    我创建了一个用于为 arm 处理器构建我的 C++ Linux 应用程序,并将其命名为 toolchain-arm.cmake。

    它包括set(CMAKE_SYSTEM_PROCESSOR arm)。

    然后我像这样执行 CMake:

    cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE={my toolchain cmake path}/toolchain-arm.cmake {my source path}

    在我的项目的 CMakeList.txt 中,我可以随意引用 CMAKE_SYSTEM_PROCESSOR。

    在为 X86 构建时,我不包含对 -DCMAKE_TOOLCHAIN_FILE 的引用,从而使 CMAKE_SYSTEM_PROCESSOR 未定义,或者至少未定义为 arm。

    这是我的 toolchain-arm.cmake

    SET(CMAKE_SYSTEM_NAME Linux)
    SET(CMAKE_SYSTEM_VERSION 1)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    
    # specify the cross compiler
    SET(ENV{TOOLCHAIN_ROOT} /home/user/toolchain/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin )
    SET(CMAKE_C_COMPILER   $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)
    SET(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)
    
    # search for programs in the build host directories
    SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    # for libraries and headers in the target directories
    SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    

    【讨论】:

    • 鉴于大多数系统默认设置CMAKE_SYSTEM_VERSION,然后覆盖它是为交叉编译保留的特殊情况。
    • "...最好让 CMake 知道它正在构建的 ARCH/工具链。..." - CMake 是构建系统。它应该检测平台并告诉我们它是什么;反之亦然。
    【解决方案5】:

    这篇文章很旧,如果在这里复活死者,我很抱歉,但我只是想分享我的解决方案。

    我不想使用任何外部应用程序,不幸的是,我们使用的 toolchain.cmake 文件没有在另一个变量中设置 arch,所以我通过查看 CMAKE_C_FLAGS 和 CMAKE_CXX_FLAGS 变量来检测它寻找 GCC 的 -march 参数。如果没有,则回退到CMAKE_HOST_SYSTEM_PROCESSOR。

    快速浏览一下 Clang 文档似乎表明这不适用于该文档,但它只需要第二个正则表达式步骤来匹配其预期参数。

    set(TARGET_ARCH_REGEX "^.*-march[= ]([^ ]+).*$")
    string(REGEX MATCH "${TARGET_ARCH_REGEX}" TARGET_ARCH_MATCH ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS})
    if (TARGET_ARCH_MATCH)
        string(REGEX REPLACE "${TARGET_ARCH_REGEX}" "\\1" TARGET_ARCH ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS})
    else()
        set(TARGET_ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR})
    endif()
    

    【讨论】:

    • 谢谢!这行得通
    【解决方案6】:

    目前,您不需要任何技巧来确定目标架构:每个目标变量 OSX_ARCHITECTURES 已添加到 cmake 中,可用于您的目的: http://public.kitware.com/Bug/view.php?id=8725

    【讨论】:

      【解决方案7】:

      我认为最简单和最可靠的解决方案是手动指定架构,用于您正在构建的平台(您可以使用cmake . -DMY_ARCHITECTURE=x86 或类似的东西)。至少这就是我们在项目中所做的,因为您在上面描述的相同问题

      【讨论】:

      • qmake 不是 cmake。据我所知,CMake 没有适用于架构的“适当”变量。但是如果你真的想要这个,你可以编写一个 C sn-p 来检测你的架构并从 CMake 运行它并将输出解析为一个变量。有可能,我正在做类似的事情来检测 glibc 版本。
      • 只是一个旁注:显然 qmake 实际上不能在大多数情况下执行此操作... QMAKE_TARGET.arch 仅适用于 MSVC。
      【解决方案8】:

      这是了解主机架构的一种经过充分测试的方法:

      # Store in CMAKE_DEB_HOST_ARCH var the current build architecture
      execute_process(COMMAND
        dpkg-architecture
          -qDEB_HOST_ARCH
        OUTPUT_VARIABLE
          CMAKE_DEB_HOST_ARCH
        OUTPUT_STRIP_TRAILING_WHITESPACE
      )
      

      稍后根据需要在 CMakeLists 中使用该信息

      if(${CMAKE_DEB_HOST_ARCH} MATCHES "armhf")
        ...
      elseif(${CMAKE_DEB_HOST_ARCH} MATCHES "i386")
        ...
      else()
        ...
      endif()
      

      【讨论】:

      • 但这确实不适用于非基于 debian 的发行版;)
      • 绝对同意 ;)
      猜你喜欢
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多