【发布时间】:2017-01-08 13:18:03
【问题描述】:
我有一个使用 cmake 的项目,一个目标设置为仅使用 MSVC 构建:
if (MSVC)
add_library(test SHARED source.cpp)
endif()
现在另一个问题是这个目标只为 MSVC 32 位设计。那么如何检测到生成器是 MSVC64 并跳过这个目标呢?
【问题讨论】:
标签: visual-studio cmake
我有一个使用 cmake 的项目,一个目标设置为仅使用 MSVC 构建:
if (MSVC)
add_library(test SHARED source.cpp)
endif()
现在另一个问题是这个目标只为 MSVC 32 位设计。那么如何检测到生成器是 MSVC64 并跳过这个目标呢?
【问题讨论】:
标签: visual-studio cmake
检查您是否为 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 库来说并不是一个真正的选择。
try_compile() 不是一个通用命令,当你有特定的事情你想要尝试编译时使用它。 cmake 内部使用它来确定void* 的大小。 CMAKE_SIZEOF_VOID_P 没有在 project 命令之前设置,这很可能是你的错误。
有几种方法 - CMake 本身也使用 - 将检查“非 64 位”:
if(NOT "${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
...
endif()
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
...
endif()
if(NOT CMAKE_CL_64)
...
endif()
参考文献
【讨论】:
cmake 3.14 release,现在生成器名称中的位数被删除:cmake.org/cmake/help/v3.14/generator/…
CMAKE_GENERATOR 代码。此外,CMAKE_SIZEOF_VOID_P 也不起作用。即使在构建 32 位应用程序时,它也始终是 64 位的。 CMAKE_CL_64 似乎最好(当被理解时):gitlab.kitware.com/cmake/cmake/issues/16504
CMAKE_CL_64 有其自身的问题,这里的人声称如果您使用 64 位 Windows 运行,则根本没有设置它编译器:itk.org/Bug/print_bug_page.php?bug_id=9065(我可以确认我找不到该函数的任何值)。叹息......这是一团糟。也许我会尝试编写一些可靠地扫描CMAKE_GENERATOR 以获取我刚刚描述的最新32 位关键字的东西,否则视为64 位......但即使在那个解决方案中,ARM 与ARM64 将会有风险(不希望他们被视为相同)。
CMAKE_CL_64 提供了此描述:“不鼓励。改用 CMAKE_SIZEOF_VOID_P。”
在最新版本的 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"
自己也遇到了类似的问题,发现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 ()
【讨论】:
这是我最初开始编写的一个脚本,用于检测目标是 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)(如果您对编译器感兴趣)等等,或者根据具体情况独立。
(我有一个用于检查特定操作系统的类似脚本,它能够检测特定事物并返回诸如watchOS 与iOS 与macOS 或Windows 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
【讨论】:
这个例子在 cmake 3.17.0 上对我有用:
if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
....
endif()
if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")
...
endif()
【讨论】:
可选的解决方案是根据使用的编译器名称构建条件。
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
【讨论】: