Nixpkgs 24.11 makes the Apple SDK a coherent build input
Vicuña brings a redesigned Darwin build environment, Nix 2.24 and Rust-based system activation. The SDK changes replace separate framework inputs with a selected platform SDK.
Choosing a newer Apple SDK should select a coherent build environment. It should not leave a package mixing framework fragments from different SDK versions. Nixpkgs 24.11 changes its Darwin packaging model around that problem, providing complete SDK packages and letting the build environment select the appropriate one.
The public release announcement on 30 November highlights the Darwin rework as part of NixOS 24.11 Vicuña. NixOS is the Linux distribution, while Nixpkgs supplies packages usable on other Linux systems and macOS. The Darwin changes belong to that shared package set, so they matter even to someone who does not run the NixOS operating system on the machine doing the build.
I like Nix's insistence that the inputs to an environment should be describable. Platform SDK selection is an important example of that principle. A package should be able to state the platform definitions it needs without inheriting an accidental mixture from unrelated fragments.
Complete SDKs replace separate framework fragments
The Darwin maintainers' SDK announcement, published on 31 October, explains the new pattern. Frameworks and libraries now arrive together in an SDK usable with xcrun and xcbuild. The standard environment provides a default through the unversioned apple-sdk package.
A package can select a different SDK by adding it to buildInputs. That makes SDK choice a normal build input instead of requiring a separate override of the build tool's internal SDK setting. The announcement specifically says that changing xcbuild's sdkVersion is no longer required for this purpose.
The selection mechanism matters because a platform SDK is a related set of definitions. Choosing individual framework packages from incompatible SDK arrangements can create a build environment no platform vendor intended. The maintainers identify accidental SDK mixing as a persistent problem in the previous pattern.
The SDK also propagates some supporting packages automatically. The announcement names libiconv, libresolv and libsbuf among them. Packages no longer need the same conditional Darwin additions merely to obtain those propagated dependencies. This removes repeated special handling where the SDK can supply the relationship itself.
That does not mean every library needed by an application becomes implicit. The source separately discusses cases where a package needs to link against software whose headers appear in the SDK. Detecting a header and supplying the corresponding link dependency remain distinct responsibilities for package authors.
Old framework inputs become inert during transition
The old SDK packages are replaced by stubs containing guidance to use the new pattern. According to the maintainers, these stubs allow expressions to evaluate but do not provide the old framework content. The standard environment's default SDK is intended to support packages still carrying the legacy inputs.
Making those inputs inert is deliberate. It prevents a leftover framework declaration from silently adding pieces of another SDK beside the selected one. The transition can preserve evaluation while refusing to recreate the mixed environment that the redesign is meant to eliminate.
The announcement also describes compatibility helpers that add an SDK to buildInputs on behalf of older callers. Those are migration conveniences, with direct SDK inputs identified as the preferred destination. A package that depends on the internal layout of the old framework packages still needs to change.
This illustrates a useful distinction in compatibility. An expression can continue evaluating because a named attribute still exists, while the attribute no longer performs its former operation. The build's behaviour then depends on the new default environment. A successful evaluation is therefore weaker evidence than confirming that the package uses the intended SDK.
SDK propagation can express requirements of dependencies
The maintainers give examples where a compiler or framework propagates an SDK requirement to its consumers. Go propagates the 11.3 SDK. Swift uses and propagates the 13.3 SDK in the arrangement described by the announcement. These are specific relationships, not a claim that every package should select the newest SDK.
Qt provides another example. The announcement distinguishes the SDK used to build a framework from the SDK and minimum-version requirements it propagates to dependent packages. That distinction is necessary because a dependency can impose requirements on its callers that differ from the environment used to produce the dependency itself.
The purpose of the new model is to make those relationships representable. A package can select what it needs, and a dependency can carry a requirement into another build. The result is easier to reason about when the selected inputs refer to complete SDKs instead of a loose collection of individual framework fragments.
I find that more compelling than simply increasing a default version number. A version update can fix one build while preserving an unclear mechanism. The new pattern gives package authors a common way to express platform requirements and removes one source of accidental mixing.
Build tools now use the active environment
The standard environment supplies xcrun, and the announcement says it can find binaries through the build environment's PATH. Package authors no longer need to remove every use of xcrun from upstream build systems. Hard-coded references to /usr/bin/xcrun still need adjustment to the tool supplied by the build environment.
That is an important boundary in a packaged build. A tool lookup through the declared environment can resolve the selected tool. A fixed operating-system path asks for something else, regardless of which package the expression added. Retaining the upstream tool invocation and replacing the hard-coded path are compatible changes.
The SDK's toolchain does not include every utility that old packaging happened to make available. The announcement lists tools that packages now need to add explicitly when required, including bison and flex. This moves those dependencies into nativeBuildInputs, where a package can state that it needs the tools during the build.
There is also a feature-detection consequence. A bundled SDK can expose frameworks broadly, causing configure logic to detect a capability that the package did not intend to use. The maintainers say unwanted detected features may need explicit disabling. A coherent SDK reduces version mixing, but it does not remove the need to control optional features in an upstream build.
Cross-compilation gains have a stated scope
The Darwin announcement says Darwin-to-Darwin cross-compilation now works, while warning that packages with incomplete cross-compilation support can need fixes. It specifically identifies Python's lack of Darwin-to-Darwin cross-compilation support as a limiting case for packages needing Python on the target platform.
That makes the scope narrower than claiming every Darwin package can cross-compile. The build-environment work supplies support, while individual packages and their language tooling retain constraints. The announcement also lists Linux-to-Darwin work among possible future changes, so it should not be credited as a delivered capability of this release.
The 24.11 release notes identify this as the last Nixpkgs release supporting macOS Sierra through Catalina. That support statement is distinct from an SDK version used to compile a particular package. Package-specific requirements can still be narrower than the package set's overall platform window.
The distinction also helps explain why an SDK update can require reviewing detection logic even when compilation succeeds. A build system might enable an optional framework simply because the selected SDK makes it visible. The package expression needs to express whether that optional functionality belongs in the output. A reproducible build can reliably reproduce an unintended feature selection, so determinism alone does not settle whether the selected result is the one the maintainer wants. The bundled SDK supplies consistent platform definitions, while package policy still controls which available facilities are used.
Linux activation and the package manager change too
The release-tagged notes also update Nix to 2.24 and make the Rust implementation of switch-to-configuration the default for system activation. These changes belong to different parts of the system from the Darwin SDK rework.
The old Perl activation script remains available through system.switch.enableNg = false in this release. That is a transition option for activation behaviour, without implying that Nixpkgs packages generally stop using Perl. The notes separately introduce Userborn as another option for declarative user management.
For the Darwin package author, the central improvement is a more coherent way to select and propagate the platform SDK. It reduces reliance on framework-package internals and lets build tools use the active declared environment. That is a concrete change in how a package expresses its platform assumptions, extending the reproducible-build model into a part of the toolchain that previously required substantial special handling.