Building wheels for different macOS versions

Projects such as numpy and Cython provide wheels for macOS 10.13 (High Sierra), so I assume that it’s not an unreasonable thing to do. But how do I do it? The system I’d like to do that on is macOS 15 (Sequoia) on ARM with Apple clang 17.0.0.

On Linux, my approach would be to pull a docker image of an old system, ideally manylinux2014, start a container, and build the wheels in that container. What is the correct approach on macOS?

I had the impression that the environment variable MACOSX_DEPLOYMENT_TARGET and the compiler flags -mmacos-version-min and --target could help with that, but they don’t seem to do anything interesting. They seem to influence the contents of the load comands LC_BUILD_VERSION and LC_VERSION_MIN_MACOSX, but I don’t entirely understand what it means. Apparently, no combination of parameters causes LC_BUILD_VERSION to drop below 11 (Big Sur). But one can require ridiculously old PPC32-only versions (like 10.0), and there will be no error: --target=x86_64-apple-macos10.0 produces a warning, -mmacos-version-min=10.0 doesn’t even warn. I think it’s highly improbable that whatever I have built will actually run on Mac OS X 10.0. One can also require absurdly high, not-yet-existent macOS versions (for some reason 429 is allowed but 420 causes a fatal error) and there won’t be a warning, either. The resulting binary will even run despite the system definitely not being macOS 429.

Explanations and recommendations will be appreciated.

2 Likes

MACOSX_DEPLOYMENT_TARGET does do what you want here.

If you’re building on arm64, macOS 11 was the first version to support that, which is why the deployment target won’t go any lower.

If you build on an x86-64 machine or cross-compile to x86-64, then 10.13 will be honored. If you’re building in CI on GitHub, macos-13 is the last runner version that gives you x86-64 instances.

1 Like

You may also need to set _PYTHON_HOST_PLATFORM=macosx-10.10-x86_64 if your Python installation isn’t built for such version.

I see. When I use MACOSX_DEPLOYMENT_TARGET=10.13 add -arch x86_64, the LC_VERSION_MIN_MACOSX entry indeed becomes 10.13. But can I expect it to work? Is there a way to determine (or at least guess) whether it will work, other than finding a High Sierra install and running the tests there?

What happens if I don’t?

2 Likes

It decides it knows better and flat out ignores you with a warning that you can’t even see unless you run pip wheel/build with -v:

[WARNING] MACOSX_DEPLOYMENT_TARGET is set to a lower value (11.0) than the version on which the Python interpreter was compiled (15.0), and will be ignored
2 Likes

I think the critical part is to ensure that the test suite is run on an x86-64 machine; a macos-13 runner will do. That doesn’t 100% guarantee that it will run on 10.13, but failures are extremely rare and usually have an obvious reason like “you’re calling a system API that doesn’t exist on macOS < 11”. Every project I know of relies on bug reports to tell them that. Especially if an older release worked fine on 10.13, then it’s very unlikely that a new release won’t.

1 Like