macOS: Offer control over host architecture on Apple Silicon hosts

Since commit b6c60f14b6 (macOS: Default to arm64 architecture on Apple
Silicon hosts, 2020-09-28, v3.19.0-rc1~63^2) we use `sysctl` to detect
that we are running on Apple Silicon in a way that pierces Rosetta.
This always sets `CMAKE_HOST_SYSTEM_PROCESSOR` to be `arm64` on such
hosts.  However, macOS offers strong support for running processes under
an emulated `x86_64` architecture.

Teach CMake to select either `arm64` or `x86_64` as the host
architecture on Apple Silicon based on the architecture of its own
process.  When CMake is built as a universal binary, macOS will select
whichever slice (architecture) is appropriate under the user's shell,
and `CMAKE_HOST_SYSTEM_PROCESSOR` will match.

Also offer a `CMAKE_APPLE_SILICON_PROCESSOR` variable and environment
variable to provide users with explicit control over the host
architecture selection regardless of CMake's own architecture.

Finally, if `CMAKE_OSX_ARCHITECTURES` is not set, pass explicit flags to
the toolchain to use selected host architecture instead of letting the
toolchain pick.

Fixes: #21554
This commit is contained in:
Brad King 2020-12-08 15:20:01 -05:00
parent 0334a3c68f
commit 5f882f6ce5
8 changed files with 126 additions and 17 deletions

View File

@ -0,0 +1,13 @@
CMAKE_APPLE_SILICON_PROCESSOR
-----------------------------
.. versionadded:: 3.19.2
.. include:: ENV_VAR.txt
On Apple Silicon hosts running macOS, set this environment variable to tell
CMake what architecture to use for :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`.
The value must be either ``arm64`` or ``x86_64``.
The :variable:`CMAKE_APPLE_SILICON_PROCESSOR` normal variable, if set,
overrides this environment variable.

View File

@ -28,6 +28,7 @@ Environment Variables that Control the Build
.. toctree::
:maxdepth: 1
/envvar/CMAKE_APPLE_SILICON_PROCESSOR
/envvar/CMAKE_BUILD_PARALLEL_LEVEL
/envvar/CMAKE_CONFIG_TYPE
/envvar/CMAKE_EXPORT_COMPILE_COMMANDS

View File

@ -353,6 +353,7 @@ Variables that Control the Build
/variable/CMAKE_ANDROID_SKIP_ANT_STEP
/variable/CMAKE_ANDROID_STANDALONE_TOOLCHAIN
/variable/CMAKE_ANDROID_STL_TYPE
/variable/CMAKE_APPLE_SILICON_PROCESSOR
/variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
/variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG
/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS

View File

@ -53,6 +53,22 @@ Languages
* ``CUDA`` language support now works on QNX.
Platforms
---------
* Apple Silicon is now supported (since CMake 3.19.2):
* The :variable:`CMAKE_HOST_SYSTEM_PROCESSOR` is selected using ``uname -m``.
Since this may vary based on CMake's own architecture and that of
the invoking process tree, the :variable:`CMAKE_APPLE_SILICON_PROCESSOR`
variable or :envvar:`CMAKE_APPLE_SILICON_PROCESSOR` environment
variable may be set to specify a host architecture explicitly.
* If :variable:`CMAKE_OSX_ARCHITECTURES` is not set, CMake adds explicit
flags to tell the compiler to build for the
:variable:`CMAKE_HOST_SYSTEM_PROCESSOR` so the toolchain does not
have to guess based on the process tree's architecture.
File-Based API
--------------
@ -357,3 +373,11 @@ Changes made since CMake 3.19.0 include the following.
It requires macOS 10.10 or newer.
The package file naming pattern has been changed from
``cmake-$ver-Darwin-x86_64`` to ``cmake-$ver-macos-universal``.
* Apple Silicon host architecture selection support was updated.
CMake 3.19.0 and 3.19.1 always chose ``arm64`` as the host architecture.
CMake 3.19.2 returns to using ``uname -m`` as CMake 3.18 and below did.
Since this may vary based on CMake's own architecture and that of
the invoking process tree, the :variable:`CMAKE_APPLE_SILICON_PROCESSOR`
variable or :envvar:`CMAKE_APPLE_SILICON_PROCESSOR` environment
variable may be set to specify a host architecture explicitly.

View File

@ -0,0 +1,15 @@
CMAKE_APPLE_SILICON_PROCESSOR
-----------------------------
.. versionadded:: 3.19.2
On Apple Silicon hosts running macOS, set this variable to tell
CMake what architecture to use for :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`.
The value must be either ``arm64`` or ``x86_64``.
The value of this variable should never be modified by project code.
It is meant to be set by a toolchain file specified by the
:variable:`CMAKE_TOOLCHAIN_FILE` variable, or as a cache entry
provided by the user, e.g. via ``-DCMAKE_APPLE_SILICON_PROCESSOR=...``.
See also the :envvar:`CMAKE_APPLE_SILICON_PROCESSOR` environment variable.

View File

@ -3,13 +3,40 @@ CMAKE_HOST_SYSTEM_PROCESSOR
The name of the CPU CMake is running on.
On Windows, this variable is set to the value of the environment variable
``PROCESSOR_ARCHITECTURE``. On systems that support ``uname``, this variable is
set to the output of:
Windows Platforms
^^^^^^^^^^^^^^^^^
- ``uname -m`` on GNU, Linux, Cygwin, Darwin, Android, or
On Windows, this variable is set to the value of the environment variable
``PROCESSOR_ARCHITECTURE``.
Unix Platforms
^^^^^^^^^^^^^^
On systems that support ``uname``, this variable is set to the output of:
- ``uname -m`` on GNU, Linux, Cygwin, Android, or
- ``arch`` on OpenBSD, or
- on other systems,
* ``uname -p`` if its exit code is nonzero, or
* ``uname -m`` otherwise.
macOS Platforms
^^^^^^^^^^^^^^^
The value of ``uname -m`` is used by default.
On Apple Silicon hosts, the architecture printed by ``uname -m`` may vary
based on CMake's own architecture and that of the invoking process tree.
.. versionadded:: 3.19.2
On Apple Silicon hosts:
* The :variable:`CMAKE_APPLE_SILICON_PROCESSOR` variable or
the :envvar:`CMAKE_APPLE_SILICON_PROCESSOR` environment variable
may be set to specify the host architecture explicitly.
* If :variable:`CMAKE_OSX_ARCHITECTURES` is not set, CMake adds explicit
flags to tell the compiler to build for the host architecture so the
toolchain does not have to guess based on the process tree's architecture.

View File

@ -43,25 +43,44 @@ if(CMAKE_HOST_UNIX)
else()
exec_program(${CMAKE_UNAME} ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
endif()
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|CYGWIN.*|Darwin|^GNU$|Android")
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|CYGWIN.*|^GNU$|Android")
exec_program(${CMAKE_UNAME} ARGS -m OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_PROCESSOR
RETURN_VALUE val)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
# Check whether we are running under Rosetta on arm64 hardware.
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
# If we are running on Apple Silicon, honor CMAKE_APPLE_SILICON_PROCESSOR.
if(DEFINED CMAKE_APPLE_SILICON_PROCESSOR)
set(_CMAKE_APPLE_SILICON_PROCESSOR "${CMAKE_APPLE_SILICON_PROCESSOR}")
elseif(DEFINED ENV{CMAKE_APPLE_SILICON_PROCESSOR})
set(_CMAKE_APPLE_SILICON_PROCESSOR "$ENV{CMAKE_APPLE_SILICON_PROCESSOR}")
else()
set(_CMAKE_APPLE_SILICON_PROCESSOR "")
endif()
if(_CMAKE_APPLE_SILICON_PROCESSOR)
if(";${_CMAKE_APPLE_SILICON_PROCESSOR};" MATCHES "^;(arm64|x86_64);$")
execute_process(COMMAND sysctl -q hw.optional.arm64
OUTPUT_VARIABLE _sysctl_stdout
ERROR_VARIABLE _sysctl_stderr
RESULT_VARIABLE _sysctl_result
)
if(_sysctl_result EQUAL 0 AND _sysctl_stdout MATCHES "hw.optional.arm64: 1")
set(CMAKE_HOST_SYSTEM_PROCESSOR "arm64")
if(NOT _sysctl_result EQUAL 0 OR NOT _sysctl_stdout MATCHES "hw.optional.arm64: 1")
set(_CMAKE_APPLE_SILICON_PROCESSOR "")
endif()
elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "Power Macintosh")
# OS X ppc 'uname -m' may report 'Power Macintosh' instead of 'powerpc'
set(CMAKE_HOST_SYSTEM_PROCESSOR "powerpc")
unset(_sysctl_result)
unset(_sysctl_stderr)
unset(_sysctl_stdout)
endif()
endif()
if(_CMAKE_APPLE_SILICON_PROCESSOR)
set(CMAKE_HOST_SYSTEM_PROCESSOR "${_CMAKE_APPLE_SILICON_PROCESSOR}")
else()
exec_program(${CMAKE_UNAME} ARGS -m OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_PROCESSOR
RETURN_VALUE val)
endif()
unset(_CMAKE_APPLE_SILICON_PROCESSOR)
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "Power Macintosh")
# OS X ppc 'uname -m' may report 'Power Macintosh' instead of 'powerpc'
set(CMAKE_HOST_SYSTEM_PROCESSOR "powerpc")
endif()
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "OpenBSD")
exec_program(arch ARGS -s OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_PROCESSOR
RETURN_VALUE val)

View File

@ -22,13 +22,22 @@ set(CMAKE_OSX_ARCHITECTURES "$ENV{CMAKE_OSX_ARCHITECTURES}" CACHE STRING
if(NOT CMAKE_CROSSCOMPILING AND
CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND
CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64" AND
CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
# When building on Apple Silicon (arm64), we need to explicitly specify
CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|x86_64)$")
execute_process(COMMAND sysctl -q hw.optional.arm64
OUTPUT_VARIABLE _sysctl_stdout
ERROR_VARIABLE _sysctl_stderr
RESULT_VARIABLE _sysctl_result
)
# When building on an Apple Silicon host, we need to explicitly specify
# the architecture to the toolchain since it will otherwise guess the
# architecture based on that of the build system tool.
# Set an *internal variable* to tell the generators to do this.
set(_CMAKE_APPLE_ARCHS_DEFAULT "arm64")
if(_sysctl_result EQUAL 0 AND _sysctl_stdout MATCHES "hw.optional.arm64: 1")
set(_CMAKE_APPLE_ARCHS_DEFAULT "${CMAKE_HOST_SYSTEM_PROCESSOR}")
endif()
unset(_sysctl_result)
unset(_sysctl_stderr)
unset(_sysctl_stdout)
endif()
# macOS, iOS, tvOS, and watchOS should lookup compilers from