Here is a script I originally started writing to detect whether a target was x86
(32-bit x86) or amd64
(64-bit x86). I then found Jake Petroules' script and continued to assimilate bits and pieces from multiple sources scoured off the Internet to detect target architectures in general using the C compiler. There are many different C compilers and they give many inconsistent answers back. This script is designed to handle them all (in theory), and give CMake much higher detection fidelity. It works by using the preprocessor to communicate back to the CMake script. Put it in your CMake modules folder and include()
it at least once in your project (it has an include guard so multiple inclusions are okay). Then you will have a variable CMAKE_CPU_ARCHITECTURES
in the folder where you included()
this script, and all subfolders. This variable is a list (because XCode project files can target multiple architectures at once). You should check to see if the one you want is in this list.
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.
...
It will either be x86
(32 bit) or amd64
(64 bit). This is only the target architecture (i.e. the CPU chip). To detect operating system, generator, compiler, etc., is another story, but you can use the architecture check nested inside various other if-statement, e.g. if(WIN32)
(confusingly just means targeting some kind of Windows, not 32-bit necessarily) or if(MSVC)
(if you're interested in the compiler), and so forth, or whatever as the case may be, independently.
(I have a similar script for checking specific operating systems, and it is able to detect specific things and returns values like watchOS
vs. iOS
vs. macOS
or Windows CE
, and so on, but that's another story).
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:
# https://mcmap.net/q/223691/-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