Skip to content

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.

Edited by alcroito
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information