【问题标题】:How to detect if 64 bit MSVC with cmake?如何使用 cmake 检测 64 位 MSVC?
【发布时间】:2017-01-08 13:18:03
【问题描述】:

我有一个使用 cmake 的项目,一个目标设置为仅使用 MSVC 构建:

 if (MSVC)
     add_library(test SHARED source.cpp) 
 endif()

现在另一个问题是这个目标只为 MSVC 32 位设计。那么如何检测到生成器是 MSVC64 并跳过这个目标呢?

【问题讨论】:

    标签: visual-studio cmake


    【解决方案1】:

    检查您是否为 64 位架构生成的常用方法是测试 CMAKE_SIZEOF_VOID_P

    project( ... )
    ...
    
    # won't work before project()!    
    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
        # 64 bits
    elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
        # 32 bits
    endif()
    

    此变量仅在project 命令之后设置!

    【讨论】:

    • 这个命令是否可靠,看起来CMAKE_SIZEOF_VOID_P 没有填充任何值。我怀疑您需要调用 try_compile 才能使其正常工作,但这对于仅标头的 INTERFACE 库来说并不是一个真正的选择。
    • @DavidLedger try_compile() 不是一个通用命令,当你有特定的事情想要尝试编译时使用它。 cmake 内部使用它来确定void* 的大小。 CMAKE_SIZEOF_VOID_P 没有在 project 命令之前设置,这很可能是你的错误。
    【解决方案2】:

    有几种方法 - CMake 本身也使用 - 将检查“非 64 位”:

    if(NOT "${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
        ...
    endif()
    
    if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
        ...
    endif()
    
    if(NOT CMAKE_CL_64)
       ...
    endif()
    

    参考文献

    【讨论】:

    • if(NOT "${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)") 似乎是我所追求的,我没有尝试 CMAKE_SIZEOF_VOID_P 但我想这也应该工作
    • 当心cmake 3.14 release,现在生成器名称中的位数被删除:cmake.org/cmake/help/v3.14/generator/…
    • 2019:上面的 CMAKE_GENERATOR 代码已经过时了。 Visual Studio 2019 的生成器没有位数后缀(默认为 64 位),除非用户在 CMake 中手动指定“生成器目标平台”。并且平台已重命名为 ARM64、ARM、Win32 和 x64。这不是寻找目标生成器平台的可靠方法...不要使用上面的CMAKE_GENERATOR 代码。此外,CMAKE_SIZEOF_VOID_P 也不起作用。即使在构建 32 位应用程序时,它也始终是 64 位的。 CMAKE_CL_64 似乎最好(当被理解时):gitlab.kitware.com/cmake/cmake/issues/16504
    • 2019 PART 2:但是,CMAKE_CL_64 有其自身的问题,这里的人声称如果您使用 64 位 Windows 运行,则根本没有设置它编译器:itk.org/Bug/print_bug_page.php?bug_id=9065(我可以确认我找不到该函数的任何值)。叹息......这是一团糟。也许我会尝试编写一些可靠地扫描CMAKE_GENERATOR 以获取我刚刚描述的最新32 位关键字的东西,否则视为64 位......但即使在那个解决方案中,ARMARM64 将会有风险(不希望他们被视为相同)。
    • Documentation 对于 CMake 的最新版本(当时为 3.17),为 CMAKE_CL_64 提供了此描述:“不鼓励。改用 CMAKE_SIZEOF_VOID_P。”
    【解决方案3】:

    在最新版本的 CMake/Visual Studio 中,位数使用 CMAKE_GENERATOR_PLATFORM 选择,可以在命令行中使用 -A 选项指定:

    cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_BUILD_TYPE=Release ..
    

    因此,基于此功能,您可以从 CMakeLists.txt 中查询值:

    if(NOT ("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win64"))
        ...
    ENDIF()
    

    【讨论】:

    • cmake 3.22.0 是必需的:"${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64"
    【解决方案4】:

    自己也遇到了类似的问题,发现CMAKE_VS_PLATFORM_NAME,是根据生成器计算出来的,我觉得值得一提。

    我已经尝试使用 cmake 3.16.1(根据文档,它从 3.1.3 开始可用)使用:-GVisual Studio 15 2017-GVisual Studio 15 2017 Win64-GVisual Studio 15 2017 -Ax64,它被设置为:Win32、@分别为 987654326@ 和 x64。因此,对于这种情况,我认为检查 Win32 应该可以工作,例如

    if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
        ...
    endif ()
    

    【讨论】:

      【解决方案5】:

      这是我最初开始编写的一个脚本,用于检测目标是 x86(32 位 x86)还是 amd64(64 位 x86)。然后,我找到了 Jake Petroules 的脚本,并继续从 Internet 上搜索的多个来源中吸收点点滴滴,以检测一般使用 C 编译器的目标体系结构。有许多不同的 C 编译器,它们会给出许多不一致的答案。该脚本旨在处理所有这些(理论上),并为 CMake 提供更高的检测保真度。它通过使用预处理器与 CMake 脚本进行通信来工作。把它放在你的 CMake 模块文件夹中,include() 在你的项目中至少一次(它有一个包含保护,所以多个包含是可以的)。然后,您将在 included() 此脚本所在的文件夹和所有子文件夹中拥有一个变量 CMAKE_CPU_ARCHITECTURES。这个变量是一个列表(因为 XCode 项目文件可以同时针对多个架构)。您应该检查一下您想要的是否在此列表中。

      if("amd64" IN_LIST CMAKE_CPU_ARCHITECTURES) # I am targeting 64-bit x86.
      ...
      if("x86" IN_LIST CMAKE_CPU_ARCHITECTURES) # I am targeting 32-bit x86.
      ...
      

      它将是x86(32 位)或amd64(64 位)。这只是目标架构(即 CPU 芯片)。检测操作系统、生成器、编译器等是另一回事,但您可以使用嵌套在各种其他 if 语句中的架构检查,例如if(WIN32)(令人困惑的只是意味着针对某种 Windows,不一定是 32 位)或 if(MSVC)(如果您对编译器感兴趣)等等,或者根据具体情况独立。

      (我有一个用于检查特定操作系统的类似脚本,它能够检测特定事物并返回诸如watchOSiOSmacOSWindows CE 之类的值,等等,但是那是另一个故事)。

      detect_cpu_architectures.cmake:

      if(NOT detect_cpu_architectures_INC) # include guard
      set(detect_cpu_architectures_INC)
      
      # This code was originally written by Jake Petroules,
      # https://github.com/axr/solar-cmake/blob/master/TargetArch.cmake
      
      # then edited/extended by Daniel Russell with help from
      # https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
      # https://opensource.apple.com/source/WTFEmbedded/WTFEmbedded-95/wtf/Platform.h.auto.html
      # http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0098a/armccref_babjidgc.htm
      # https://sourceforge.net/p/predef/wiki/Architectures/
      # https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html
      
      # Regarding POWER/PowerPC, just as is noted in the Qt source,
      # "There are many more known variants/revisions that we do not handle/detect."
      # Set ppc_support to TRUE before including this file or ppc and ppc64
      # will be treated as invalid architectures since they are no longer supported by Apple
      
      # TODO: add more chips!!!! Tedious though.    
      enable_language(C) # this must be at file-scope not function scope. Thus include-guard.
      
      set(archdetect_c_code "
      /* -----------------------------------ARM ---------------------------------- */
      
      #if defined(__ARM64_ARCH_8__) || defined(__aarch64__) || defined(__ARMv8__) || defined(__ARMv8_A__) || defined(_M_ARM64)
          #if defined(__thumb__) || defined(_M_ARMT)
              #error cmake_ARCH armthumbv3
          #elif
              #error cmake_ARCH armv8
          #endif
      
      #elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__  ) || defined(__ARM_ARCH_7F__) || defined(__ARM_ARCH_7S__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7)
          #if defined(__thumb__) || defined(__thumb2__) || defined(_M_ARMT)
              #error cmake_ARCH armthumbv2
          #elif
              #error cmake_ARCH armv7
          #endif
      
      #elif defined(__ARM_ARCH_6__) ||  defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6M__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6)
          #if defined(__thumb__) || defined(_M_ARMT)
              #error cmake_ARCH armthumbv1
          #elif
              #error cmake_ARCH armv6
          #endif
      
      #elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5E__ ) || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) || defined(__MARM_ARMV5__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5)
          #error cmake_ARCH armv5
      
      #elif defined(__ARM_ARCH_4__) ||  defined(__ARM_ARCH_4T__) || defined(__MARM_ARMV4__)
          #error cmake_ARCH armv4
      
      #elif defined(__TARGET_ARCH_ARM)
          #error cmake_ARCH armv ## __TARGET_ARCH_ARM
      
      #elif defined(__arm__) || defined(_M_ARM)
          #error arm
      
      /* ----------------------------------- x86 ---------------------------------- */
      
      #elif defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(i386)  || defined(_M_IX86) || defined(_X86_) || defined(__THW_INTEL)
          #error cmake_ARCH x86
      
      #elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) || defined(__amd64__) || defined(_M_AMD64)
          #error cmake_ARCH amd64
      
      // AMD wrote the original x86_64 bit spec after cloning x86 for years.
      // Then, in 2003, they renamed it to amd64. So basically x64 ~ amd64 ~ x86_64
      // But the trend seems to be to tidy this up and go with the most officialish, amd64.
      // Think of it this way, AMD was in fact the one pushing the architecture forwards at times.
      // Even if Intel invented it. Therefore, I say, let's stick with amd64 and be over this mess already.
      
      /* ------------------------------- Itanium ---------------------------------- */
      
      #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) || defined(_IA64) || defined(__IA64__) || defined(__itanium__)
          /* 32-bit mode on Itanium */
          #if !defined(__LP64__) && !defined(_M_X64) /* <-- Visual Studio macro (it USED to support Itanium) */
              #error cmake_ARCH ia32
          #else
              #error cmake_ARCH ia64
          #endif
      
      /* ------------------------------ Power PC  --------------------------------- */
      
      #elif defined(__ppc__) || defined(__ppc) || defined(__PPC__)  || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_M_MPPC) || defined(_M_PPC) || defined(__POWERPC__)
          #if defined(__ppc64__) || defined(__PPC64__) || defined(__powerpc64__) || defined(__64BIT__) 
              #error cmake_ARCH ppc64
          #else
              #error cmake_ARCH ppc32
          #endif
      
      /* ---------------------------------- Sparc --------------------------------- */
      
      #elif defined(__sparc) && !defined(__arch64__) || defined(__sparcv8)
          #error cmake_ARCH sparc32
      
      #elif defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)
          #error cmake_ARCH sparc64
      
      /* --------------------------- IBM Mainframes ------------------------------- */
      
      /* 64 bit IBM z/Architecture */
      #elif defined(__s390x__) || defined(__s390X__) || defined(__zarch__) || defined(__SYSC_ZARCH__)
          #error cmake_ARCH zArch
      
      /* Older 32 bit IBM System/390, maybe useful for Hercules emulation? */
      #elif defined(__s390__)
          #error cmake_ARCH s390
      
      /* --------------------------- Hitachi SuperH ------------------------------- */
      
      #elif defined(__sh1__)
          #error cmake_ARCH superH1
      #elif defined(__sh2__)
          #error cmake_ARCH superH2
      #elif defined(__sh3__) || defined(__SH3__)
          #error cmake_ARCH superH3
      #elif defined(__SH4__)
          #error cmake_ARCH superH4
      #elif defined(__SH5__)
          #error cmake_ARCH superH5
      #elif defined(__sh__) // not sure which version...unlikely?
          #error cmake_ARCH superH 
      
      /* -------------------------- Analog Devices --------------------------------- */
      
      #elif defined(__bfin) || defined(__BFIN__)
          #error cmake_ARCH blackfin
      #elif defined(__ADSPTS101__)
          #error cmake_ARCH adps-ts101
      #elif defined(__ADSPTS201__)
          #error cmake_ARCH adps-ts201
      #elif defined(__ADSPTS202__)
          #error cmake_ARCH adps-ts202
      #elif defined(__ADSPTS203__)
          #error cmake_ARCH adps-ts203
      #elif defined(__ADSPTS20x__) // this will be true for any of the above but we use as  catchall
          #error cmake_ARCH adps-ts20x
      
      /* ------------------------------- Alpha ------------------------------------- */
      
      #elif defined(__alpha_ev4__)
          #error cmake_ARCH alpha_ev4
      #elif defined(__alpha_ev5__)
          #error cmake_ARCH alpha_ev5
      #elif defined(__alpha_ev6__)
          #error cmake_ARCH alpha_ev6
      #elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA)
          #error cmake_ARCH alpha
      
      /* ------------------------- Texas Instruments ------------------------------ */
      
      #elif defined(_TMS320C6200)
          #error cmake_ARCH c6200
      #elif defined(_TMS320C6400)
          #error cmake_ARCH c6400
      #elif defined(_TMS320C6400_PLUS)
          #error cmake_ARCH c6400+
      #elif defined(_TMS320C6600)
          #error cmake_ARCH c6600
      #elif defined(_TMS320C6700)
          #error cmake_ARCH c6700
      #elif defined(_TMS320C6700_PLUS)
          #error cmake_ARCH c6700+
      #elif defined(_TMS320C6740)
          #error cmake_ARCH c6740
      #elif defined(__TMS470__)
          #error cmake_ARCH tms470
      
      /* -------------------------- Hewlett Packard ------------------------------ */
      
      #elif defined(_PA_RISC1_0)
          #error cmake_ARCH parisc1_0
      
      #elif defined(_PA_RISC1_1) || defined(__HPPA11__) || defined(__PA7100__)
          #error cmake_ARCH parisc1_1
      
      #elif defined(_PA_RISC2_0) || defined(__RISC2_0__) || defined(__HPPA20__) || defined(__PA8000__)
          #error cmake_ARCH parisc2_0
      
      #elif defined(__hppa__) || defined(__HPPA__) || defined(__hppa)
          #error cmake_ARCH parisc
      
      /* ---------------------------- Adapteva --------------------------------- */
      
      #elif defined(__epiphany__)
          #error cmake_ARCH ephiphany
      
      /* ------------------------------ MIPS  ---------------------------------- */
      
      #elif defined(_MIPS_ISA_MIPS1) || (__mips == 1)
          #error cmake_ARCH mips1
      #elif defined(_MIPS_ISA_MIPS2) || defined(__MIPS_ISA2__) || (__mips == 2)
          #error cmake_ARCH mips2
      #elif defined(_MIPS_ISA_MIPS3) || defined(__MIPS_ISA3__) || (__mips == 3)
          #error cmake_ARCH mips3
      #elif defined(_MIPS_ISA_MIPS4) || defined(__MIPS_ISA4__) || (__mips == 4)
          #error cmake_ARCH mips4
      #elif defined(__MIPS__) || defined(__mips__) || defined(mips) || defined(__mips)
          #error cmake_ARCH mips
      
      /* ---------------------------- Motorola  -------------------------------- */
      
      #elif defined(__mc68000__) || defined(__MC68000__)
          #error cmake_ARCH mc68000
      #elif defined(__mc68010__)
          #error cmake_ARCH mc68010
      #elif defined(__mc68020__) || defined(__MC68020__)
          #error cmake_ARCH mc68020
      #elif defined(__mc68030__) || defined(__MC68030__)
          #error cmake_ARCH mc68030
      #elif defined(__mc68040__) || defined(__MC68040__)
          #error cmake_ARCH mc68040
      #elif defined(__mc68060__) || defined(__MC68060__)
          #error cmake_ARCH mc68060
      #elif defined(__m68k__) || defined(M68000) || defined(__MC68K__)
          #error cmake_ARCH mc68
      
      #endif
      ")
      
      
      if(APPLE AND CMAKE_OSX_ARCHITECTURES)
          # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set
          # First let's normalize the order of the values
      
          # Note that it's not possible to compile PowerPC applications if you are using
          # the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we
          # disable it by default
          # See this page for more information:
          # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4
      
          # Architecture defaults to x86  or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime.
          # On OS X 10.6+ the default is x86_64 if the CPU supports it, x86 otherwise.
      
          foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES})
              if("${osx_arch}" STREQUAL "ppc" AND ppc_support)
                  set(osx_arch_ppc TRUE)
              elseif("${osx_arch}" STREQUAL "i386")
                  set(osx_arch_i386 TRUE)
              elseif("${osx_arch}" STREQUAL "x86_64")
                  set(osx_arch_x86_64 TRUE)
              elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support)
                  set(osx_arch_ppc64 TRUE)
              else()
                  message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}")
              endif()
          endforeach()
      
          # Now add all the architectures in our normalized order
          if(osx_arch_ppc)
              list(APPEND ARCH ppc32)
          endif()
      
          if(osx_arch_i386)
              list(APPEND ARCH x86)
          endif()
      
          if(osx_arch_x86_64)
              list(APPEND ARCH x64)
          endif()
      
          if(osx_arch_ppc64)
              list(APPEND ARCH ppc64)
          endif()
      else()
          file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/detect_cpu_arch.c" "${archdetect_c_code}")
      
      
          # Detect the architecture in a rather creative way...
          # This compiles a small C program which is a series of ifdefs that selects a
          # particular #error preprocessor directive whose message string contains the
          # target architecture. The program will always fail to compile (both because
          # file is not a valid C program, and obviously because of the presence of the
          # #error preprocessor directives... but by exploiting the preprocessor in this
          # way, we can detect the correct target architecture even when cross-compiling,
          # since the program itself never needs to be run (only the compiler/preprocessor)
          try_run(
              run_result_unused
              compile_result_unused
              "${CMAKE_BINARY_DIR}/CMakeFiles"
              "${CMAKE_BINARY_DIR}/CMakeFiles/detect_cpu_arch.c"
              COMPILE_OUTPUT_VARIABLE ARCH
              CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
          )
      
          # Parse the architecture name from the compiler output
          string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}")
      
          # Get rid of the value marker leaving just the architecture name
          string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}")
      
          # If we are compiling with an unknown architecture this variable should
          # already be set to "unknown" but in the case that it's empty (i.e. due
          # to a typo in the code), then set it to unknown
          if (NOT ARCH)
              set(ARCH unknown)
          endif()
      endif()
      
      
      set(CMAKE_CPU_ARCHITECTURES "${ARCH}")
      
      endif(NOT detect_cpu_architectures_INC) # include guard
      

      【讨论】:

      • 你在 GitHub 上的某个地方维护这个脚本吗?
      【解决方案6】:

      这个例子在 cmake 3.17.0 上对我有用:

      if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
        ....
      endif()
      
      if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")
        ...
      endif()
      

      【讨论】:

        【解决方案7】:

        可选的解决方案是根据使用的编译器名称构建条件。

        if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
        

        【讨论】:

          猜你喜欢
          • 2010-09-25
          • 2019-07-09
          • 2012-10-25
          • 1970-01-01
          • 2011-12-15
          • 2013-12-30
          • 1970-01-01
          相关资源
          最近更新 更多