macosuniversal-binaryapple-silicon

Is there any way to compile x86-64 + arm64 universal binaries in macOS 10.14?


The official way to compile universal binaries that target Apple's new M1 processor is to use Xcode 12, which requires at minimum macOS 10.15.4. I'm currently running macOS 10.14.6 though and do not want to upgrade if I can avoid it. Is there an unofficial way to compile such a universal binary when running macOS 10.14?


Solution

  • This is possible to do. Of course it can't be done with Xcode on its own since Apple has long since dropped support for macOS 10.14 in its developer tools, but can be done either by compiling from the command line or using another build system like cmake or qmake.

    It at minimum requires two things:

    You can actually get both things by downloading the latest release of Xcode, since as of the time of this writing (December 2022) the release of LLVM included in the latest release of Xcode (14.2) will still run in macOS 10.14.

    Should that ever stop working, though, or if you would prefer not to use Xcode's release of LLVM, you can also install a newer version of LLVM and clang using MacPorts, as MacPorts continues to support older macOS releases (unlike Homebrew). The command would be sudo port install clang-15, or whatever the latest available major release of clang is.

    Another option is to built and install your own copy of LLVM and clang, and the steps to do so are laid out in LLVM's documentation.

    For best results, the version of LLVM should be the same major version or newer than the version of LLVM / clang included in the release of Xcode you obtained.

    Once you have both of those, you can compile C, C++, or Objective-C and probably Swift with a command like:

    /path/to/newer/clang \
        -arch x86_64 -arch arm64 \
        -isysroot /path/to/Xcode14.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
        source.m source2.cpp source3.c ...
    

    If you installed clang using MacPorts, then its path would be something like /opt/local/bin/clang-mp-15.

    In the case of only using a newer release of Xcode, you can do it like this:

    /path/to/Xcode14.2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang \
        -arch x86_64 -arch arm64 \
        -isysroot /path/to/Xcode14.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
        source.m source2.cpp source3.c ...
    

    I've only tested this with C, C++ and Objective-C so far, and not Swift, but presumably it will work for Swift as well.

    Note that if you want to target macOS 10.14, it's still necessary to avoid using parts of the macOS SDK as well as C++ and Swift features that require macOS 10.15 or later.

    (It may be possible to at least make use of newer C++ features in macOS 10.14 and earlier if you build your own universal binaries of LLVM's runtimes (namely libc++ and libc++abi) from the later release of LLVM, and then compile your code with the arguments -nostdlib, statically link to libc++ and libc++abi, and then explicitly link to /usr/lib/libSystem.B.dylib. But so far I haven't managed to get an arm64 copy of LLVM's runtimes to compile in macOS 10.14.)