Files
Breeze/breeze/cmake/HostArch.cmake
T
2026-10-24 09:53:48 -03:00

156 lines
7.7 KiB
CMake

# =============================================================================
# HostArch.cmake
#
# One place to turn this project's own architecture labels -- x64, arm64,
# or arm32 (empty if unrecognized) -- out of a processor string. Two entry
# points, because "which architecture" has two different questions behind it:
#
# pmn_host_arch() the machine CMake is actually running ON.
# NinjaPin.cmake / DoxygenPin.cmake use it to decide
# whether a pinned-tool download exists for the machine
# running CMake -- always a HOST question, independent
# of what's being built. TargetSelection.cmake also uses
# it for Clang's GNU-style driver on Windows (see
# below for why MSVC/clang-cl need a different check,
# done separately in that file).
#
# pmn_target_arch() the machine the build is FOR, i.e.
# CMAKE_SYSTEM_PROCESSOR. TargetSelection.cmake's Linux
# DIST_TARGET detection uses it. On a native build it
# is the same answer as pmn_host_arch() (CMake sets
# CMAKE_SYSTEM_PROCESSOR from the host unless told
# otherwise); the difference is a cross build, where a
# toolchain file (breeze/cmake/toolchains/) sets it to the
# target -- asking the host there would label an
# aarch64 cross build "x64".
#
# Both share one aarch64/armv7l/... spelling list, and the platform-specific
# unreliability below is only handled once. Needs no project()/compiler
# detection, so it's safe to include from a bare `cmake -P` script.
#
# About pmn_host_arch() on each platform: on Linux it is
# CMAKE_HOST_SYSTEM_PROCESSOR, which is just `uname -m` -- reliable, with
# one catch that used to break the pinned-tool download on every Linux
# machine: `cmake -P` (script mode -- how breeze/scripts/setup.sh runs
# FetchNinja.cmake and FetchDoxygen.cmake) never runs CMake's system
# detection, so CMAKE_HOST_SYSTEM_PROCESSOR is simply not defined there.
# In that case this asks the OS itself, with `uname -m`. On macOS it is
# the same `uname -m`, corrected for Rosetta 2 (see the comment inside the
# function). On
# Windows it deliberately does NOT use CMAKE_HOST_SYSTEM_
# PROCESSOR (or CMAKE_SYSTEM_PROCESSOR): both are just the
# PROCESSOR_ARCHITECTURE environment variable, which reports the
# architecture of the CURRENT PROCESS, not the machine -- a 32-bit
# cmake.exe (still a completely ordinary, common download; nothing exotic)
# running under WoW64 on an ordinary 64-bit machine reports "x86" here even
# though the machine itself is x64 or ARM64. That's the same class of
# problem as the MSVC/clang-cl architecture-detection trap in
# TargetSelection.cmake (CMake issue #25466: gitlab.kitware.com/cmake/cmake/-/issues/25466),
# just via WoW64 process redirection rather than a cross-targeting
# compiler -- confirmed the hard way, as exactly what broke the pinned-
# ninja/-doxygen lookup below on an entirely ordinary 64-bit machine with a
# 32-bit cmake.exe on PATH. PROCESSOR_ARCHITEW6432 is what Windows itself
# sets to expose the TRUE architecture to a WoW64-redirected 32-bit
# process; it's simply unset outside WoW64, which is exactly when
# PROCESSOR_ARCHITECTURE was already correct anyway -- so preferring it
# when present, falling back to PROCESSOR_ARCHITECTURE otherwise, is
# correct either way.
# =============================================================================
include_guard(GLOBAL)
# _pmn_arch_label(<processor-string> <out-var>)
#
# The one place the aarch64/x86_64/armv7l/... spellings are mapped to this
# project's x64/arm64/arm32 labels. <out-var> gets one of those, or "" if
# unrecognized.
function(_pmn_arch_label PROCESSOR OUT_VAR)
if(PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
set(${OUT_VAR} "arm64" PARENT_SCOPE)
elseif(PROCESSOR MATCHES "^(x86_64|amd64|AMD64)$")
set(${OUT_VAR} "x64" PARENT_SCOPE)
elseif(PROCESSOR MATCHES "^[Aa][Rr][Mm]")
# Every other spelling of 32-bit ARM this project has seen: armv6l
# (Raspberry Pi 1/Zero), armv7l (the common case -- Raspberry Pi
# 2/3/4 32-bit OS, generic armhf), armv8l (a 32-bit personality/
# chroot on 64-bit ARM hardware), or bare "arm"/"ARM". Checked
# after arm64/aarch64 above since those also start with "arm".
set(${OUT_VAR} "arm32" PARENT_SCOPE)
else()
set(${OUT_VAR} "" PARENT_SCOPE)
endif()
endfunction()
# pmn_host_arch(<out-var> [<raw-out-var>])
#
# <out-var> gets one of x64/arm64/arm32/"" (unrecognized) for the machine
# running CMake. The optional <raw-out-var> gets whatever raw string was
# actually checked to produce that -- callers building an error message for
# the "" case should use this rather than assuming which underlying
# variable/environment variable was consulted, since that differs by
# platform (see above).
function(pmn_host_arch OUT_VAR)
if(CMAKE_HOST_WIN32)
if(NOT "$ENV{PROCESSOR_ARCHITEW6432}" STREQUAL "")
set(_pmn_ha_processor "$ENV{PROCESSOR_ARCHITEW6432}")
else()
set(_pmn_ha_processor "$ENV{PROCESSOR_ARCHITECTURE}")
endif()
elseif(CMAKE_HOST_SYSTEM_PROCESSOR)
set(_pmn_ha_processor "${CMAKE_HOST_SYSTEM_PROCESSOR}")
elseif(CMAKE_HOST_UNIX)
# Script mode (cmake -P): not defined -- see the header. This is
# exactly what CMake itself does to fill it in during a configure.
execute_process(COMMAND uname -m
OUTPUT_VARIABLE _pmn_ha_processor
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
else()
set(_pmn_ha_processor "${CMAKE_SYSTEM_PROCESSOR}")
endif()
# macOS: a process running under Rosetta 2 -- an x86_64 cmake on an Apple
# Silicon Mac, entirely ordinary since Intel builds of CMake and of most
# developer tools still circulate -- is told it's on an x86_64 machine,
# by `uname -m` and so by CMAKE_HOST_SYSTEM_PROCESSOR alike. The same
# class of trap as a 32-bit process under WoW64 on Windows (see the
# header), and fixed the same way: ask about the MACHINE.
# hw.optional.arm64 is 1 on any Apple Silicon Mac no matter whether the
# asking process is translated, and doesn't exist on Intel (-i makes a
# missing OID a silent empty answer instead of an error).
if(CMAKE_HOST_APPLE)
_pmn_arch_label("${_pmn_ha_processor}" _pmn_ha_probe)
if(_pmn_ha_probe STREQUAL "x64")
execute_process(COMMAND sysctl -in hw.optional.arm64
OUTPUT_VARIABLE _pmn_ha_arm64
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
if(_pmn_ha_arm64 STREQUAL "1")
set(_pmn_ha_processor "arm64")
endif()
endif()
endif()
if(ARGC GREATER 1)
set(${ARGV1} "${_pmn_ha_processor}" PARENT_SCOPE)
endif()
_pmn_arch_label("${_pmn_ha_processor}" _pmn_ha_label)
set(${OUT_VAR} "${_pmn_ha_label}" PARENT_SCOPE)
endfunction()
# pmn_target_arch(<out-var> [<raw-out-var>])
#
# Same contract as pmn_host_arch(), but for the machine the build is FOR:
# CMAKE_SYSTEM_PROCESSOR. Only meaningful where that variable can be
# trusted -- Linux, where it is `uname -m` for a native build and whatever
# the toolchain file says for a cross build. Not for MSVC/clang-cl, where
# it reflects the cmake.exe process instead (see the header).
function(pmn_target_arch OUT_VAR)
if(ARGC GREATER 1)
set(${ARGV1} "${CMAKE_SYSTEM_PROCESSOR}" PARENT_SCOPE)
endif()
_pmn_arch_label("${CMAKE_SYSTEM_PROCESSOR}" _pmn_ta_label)
set(${OUT_VAR} "${_pmn_ta_label}" PARENT_SCOPE)
endfunction()