Nothing in the build sets CMAKE_OSX_DEPLOYMENT_TARGET — the only matches in the tree are vendored libxml2's autoconf and an unrelated HTML file. When it is unset, CMake defaults the deployment target to the version of macOS the build happened to run on.
Two consequences:
1. The macOS floor of a release is an accident of the build machine. Whichever runner or laptop cut the build silently becomes the minimum supported version. Nothing declares that floor and nothing checks it.
2. Symbols newer than the floor link cleanly and fail at load time on users' machines. With a pinned deployment target the compiler can diagnose these (-Wunguarded-availability) and the linker can weak-link them. Unpinned, it has no version information to act on, so there is no build-time signal at all — the first sign is a user on an older Mac getting dyld: Symbol not found.
Concrete instance: #2564
quick_exit entered libSystem in macOS 15.0, and the 15 SDK declares it with no availability annotation — gated only on __DARWIN_C_LEVEL/C11, a compile-time feature gate. On a macOS 14.5 host with the 15.2 SDK:
$ cat t.cc
#include <cstdlib>
void boom() { std::quick_exit(1); }
$ clang++ -std=c++17 -c t.cc -o t.o && nm -u t.o
_quick_exit
Compiles clean, no warning, hard reference — and that binary will not load on macOS < 15. Worth noting the trigger is the SDK, not the host: a macOS 14 machine with Xcode 16 installed builds the broken binary, so "it works on my 14 box" proves nothing.
#2564 fixes that one symbol by always taking _Exit on Apple. That is a per-symbol workaround. The next post-floor API that drifts into the codebase reintroduces the same failure with the same absence of any build-time signal. Gating on __MAC_OS_X_VERSION_MIN_REQUIRED does not help either — it reads the deployment target, which follows the build host when unpinned, so it reads 150000 on exactly the builders that emit the bad reference.
Proposal
Set CMAKE_OSX_DEPLOYMENT_TARGET explicitly in the Superbuild and propagate it to every external project, so SCIRun and all of its dependencies build against one declared floor.
Blast radius is wide — it touches every Superbuild dependency — so this is its own PR rather than a rider on #2564.
Open question
What should the floor be? We have users on older Macs, so this wants a deliberate decision rather than whatever the newest runner happens to be. Whatever we pick becomes an enforced, checkable contract instead of a property of whoever ran the build.
Related: #2564, #2537
Nothing in the build sets
CMAKE_OSX_DEPLOYMENT_TARGET— the only matches in the tree are vendored libxml2's autoconf and an unrelated HTML file. When it is unset, CMake defaults the deployment target to the version of macOS the build happened to run on.Two consequences:
1. The macOS floor of a release is an accident of the build machine. Whichever runner or laptop cut the build silently becomes the minimum supported version. Nothing declares that floor and nothing checks it.
2. Symbols newer than the floor link cleanly and fail at load time on users' machines. With a pinned deployment target the compiler can diagnose these (
-Wunguarded-availability) and the linker can weak-link them. Unpinned, it has no version information to act on, so there is no build-time signal at all — the first sign is a user on an older Mac gettingdyld: Symbol not found.Concrete instance: #2564
quick_exitentered libSystem in macOS 15.0, and the 15 SDK declares it with no availability annotation — gated only on__DARWIN_C_LEVEL/C11, a compile-time feature gate. On a macOS 14.5 host with the 15.2 SDK:Compiles clean, no warning, hard reference — and that binary will not load on macOS < 15. Worth noting the trigger is the SDK, not the host: a macOS 14 machine with Xcode 16 installed builds the broken binary, so "it works on my 14 box" proves nothing.
#2564 fixes that one symbol by always taking
_Exiton Apple. That is a per-symbol workaround. The next post-floor API that drifts into the codebase reintroduces the same failure with the same absence of any build-time signal. Gating on__MAC_OS_X_VERSION_MIN_REQUIREDdoes not help either — it reads the deployment target, which follows the build host when unpinned, so it reads150000on exactly the builders that emit the bad reference.Proposal
Set
CMAKE_OSX_DEPLOYMENT_TARGETexplicitly in the Superbuild and propagate it to every external project, so SCIRun and all of its dependencies build against one declared floor.Blast radius is wide — it touches every Superbuild dependency — so this is its own PR rather than a rider on #2564.
Open question
What should the floor be? We have users on older Macs, so this wants a deliberate decision rather than whatever the newest runner happens to be. Whatever we pick becomes an enforced, checkable contract instead of a property of whoever ran the build.
Related: #2564, #2537