Inconsistent macOS architecture handling on Apple Silicon with Makefiles / Ninja generators
Preface:
In a Homebrew issue at https://github.com/Homebrew/brew/issues/7857#issuecomment-657716173 it's mentioned that on Apple silicon hardware (the DTK) the result of running uname -m
depends on whether the parent process is running under the Rosetta translation layer or not.
I've confirmed that by building a CMake targeting arm64
, and one targeting x86_64
.
Here's the results of running them on the DTK
$ cmake_intel -E env a=b uname -m
x86_64
$ cmake_arm -E env a=b uname -m
arm64
Actual issue:
This ties into what should be the default value for CMAKE_OSX_ARCHITECTURES
.
Here's a sample CMake project that i've built in 5 combinations, with latest CMake built from git.
The first 4 combinations use a Ninja
generator that was built targeting x86_64
arch.
The final 5th combo uses /usr/bin/make
which has both arm and intel slices, and thus adds additional spiciness to the chosen architecture.
cmake_minimum_required(VERSION 3.17)
project(testapp LANGUAGES CXX)
set(source_path "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
set(content "
#include <cstdlib>
#include <fstream>
#include <iostream>
int main(int argc, char**argv) {
std::system(\"uname -m > arch.txt\");
std::cout << \"Executable running under arch: \" << std::ifstream(\"arch.txt\").rdbuf(); ;
std::cout << \"\";
return 0;
}
")
file(WRITE "${source_path}" "${content}")
add_executable(myapp "${source_path}")
add_custom_command(TARGET myapp POST_BUILD
COMMAND file "$<TARGET_FILE:myapp>")
message("CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}")
message("CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
message("CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
message("CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}")
$ cmake_intel ..
-- The CXX compiler identification is AppleClang 12.0.0.12000022 ...
CMAKE_OSX_ARCHITECTURES:
CMAKE_HOST_SYSTEM_PROCESSOR: x86_64
CMAKE_SYSTEM_PROCESSOR: x86_64
CMAKE_CROSSCOMPILING: FALSE
-- Build files have been written to: /Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_intel_cmake
$ ninja
/Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_intel_cmake/myapp: Mach-O 64-bit executable x86_64
$ cmake_arm ..
CMAKE_OSX_ARCHITECTURES:
CMAKE_HOST_SYSTEM_PROCESSOR: arm64
CMAKE_SYSTEM_PROCESSOR: arm64
CMAKE_CROSSCOMPILING: FALSE
-- Build files have been written to: /Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_arm_cmake
$ ninja
/Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_arm_cmake/myapp: Mach-O 64-bit executable x86_64
$ cmake_intel -DCMAKE_OSX_ARCHITECTURES=arm64 ..
CMAKE_OSX_ARCHITECTURES: arm64
CMAKE_HOST_SYSTEM_PROCESSOR: x86_64
CMAKE_SYSTEM_PROCESSOR: x86_64
CMAKE_CROSSCOMPILING: FALSE
-- Build files have been written to: /Users/alex/Dev/projects/cmake/devkit_default_arch/arm_arch_intel_cmake
$ ninja
/Users/alex/Dev/projects/cmake/devkit_default_arch/arm_arch_intel_cmake/myapp: Mach-O 64-bit executable arm64
$ cmake_arm -DCMAKE_OSX_ARCHITECTURES=arm64 ..
CMAKE_OSX_ARCHITECTURES: arm64
CMAKE_HOST_SYSTEM_PROCESSOR: arm64
CMAKE_SYSTEM_PROCESSOR: arm64
CMAKE_CROSSCOMPILING: FALSE
-- Build files have been written to: /Users/alex/Dev/projects/cmake/devkit_default_arch/arm_arch_arm_cmake
$ ninja
/Users/alex/Dev/projects/cmake/devkit_default_arch/arm_arch_arm_cmake/myapp: Mach-O 64-bit executable arm64
$ cmake_arm .. -G"Unix Makefiles" 130 ↵
CMAKE_OSX_ARCHITECTURES:
CMAKE_HOST_SYSTEM_PROCESSOR: arm64
CMAKE_SYSTEM_PROCESSOR: arm64
CMAKE_CROSSCOMPILING: FALSE
-- Build files have been written to: /Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_arm_cmake_make
$ make
/Users/alex/Dev/projects/cmake/devkit_default_arch/default_arch_arm_cmake_make/myapp: Mach-O 64-bit executable arm64
Summarised observations:
(1) The value of CMAKE_HOST_SYSTEM_PROCESSOR
and CMAKE_SYSTEM_PROCESSOR
depends on the arch of the built CMake executable
(2) If no CMAKE_OSX_ARCHITECTURES
is specified, no specific arch flag is passed to the compiler, and thus clang
decides what arch to target based on the architecture slice of the compiler's parent build tool aka multi-arch make
vs intel-only ninja
(3) The cmake_intel -DCMAKE_OSX_ARCHITECTURES=arm64
combination has inconsistent values, in the sense that I would expect CMAKE_SYSTEM_PROCESSOR
to have the 'arm64' value. Currently the logic merely assumes that if CMAKE_CROSSCOMPILING
is false, both CMAKE_HOST_SYSTEM_PROCESSOR
and CMAKE_SYSTEM_PROCESSOR
will have the same value. It's a bit of weird edge case where the value of CMAKE_HOST_SYSTEM_PROCESSOR
and CMAKE_SYSTEM_PROCESSOR
needs to be different, despite CMAKE_CROSSCOMPILING
being false.
My proposal would be to explicitly set the value of CMAKE_OSX_ARCHITECTURES
to the value of CMAKE_HOST_SYSTEM_PROCESSOR
when nothing is passed on the command line or set in a toolchain.
That avoids the build-tool specific issue mentioned in (2).
I'm not sure yet about the rest of the peculiarities.
Finally, I expect a lot of initial confusion around this once Apple silicon is more widely available.