1# Platform Notes {#chap-platform-notes}
2
3## Darwin (macOS) {#sec-darwin}
4
5The Darwin `stdenv` differs from most other ones in Nixpkgs in a few key ways.
6These differences reflect the default assumptions for building software on that platform.
7In many cases, you can ignore these differences because the software you are packaging is already written with them in mind.
8When you do that, write your derivation as normal. You don’t have to include any Darwin-specific special cases.
9The easiest way to know whether your derivation requires special handling for Darwin is to write it as if it doesn’t and see if it works.
10If it does, you’re done; skip the rest of this.
11
12- Darwin uses Clang by default instead of GCC. Packages that refer to `$CC` or `cc` should just work in most cases.
13 Some packages may hardcode `gcc` or `g++`. You can usually fix that by setting `makeFlags = [ "CC=cc" "CXX=C++" ]`.
14 If that does not work, you will have to patch the build scripts yourself to use the correct compiler for Darwin.
15- Darwin needs an SDK to build software.
16 The SDK provides a default set of frameworks and libraries to build software, most of which are specific to Darwin.
17 There are multiple versions of the SDK packages in Nixpkgs, but one is included by default in the `stdenv`.
18 Usually, you don’t have to change or pick a different SDK. When in doubt, use the default.
19- The SDK used by your build can be found using the `DEVELOPER_DIR` environment variable.
20 There are also versions of this variable available when cross-compiling depending on the SDK’s role.
21 The `SDKROOT` variable is also set with the path to the SDK’s libraries and frameworks.
22 `SDKROOT` is always a sub-folder of `DEVELOPER_DIR`.
23- Darwin includes a platform-specific tool called `xcrun` to help builds locate binaries they need.
24 A version of `xcrun` is part of the `stdenv` on Darwin.
25 If your package invokes `xcrun` via an absolute path (such as `/usr/bin/xcrun`), you will need to patch the build scripts to use `xcrun` instead.
26
27To reiterate: you usually don’t have to worry about this stuff.
28Start with writing your derivation as if everything is already set up for you (because in most cases it already is).
29If you run into issues or failures, continue reading below for how to deal with the most common issues you may encounter.
30
31### Darwin Issue Troubleshooting {#sec-darwin-troubleshooting}
32
33#### Package requires a non-default SDK or fails to build due to missing frameworks or symbols {#sec-darwin-troubleshooting-using-sdks}
34
35In some cases, you may have to use a non-default SDK.
36This can happen when a package requires APIs that are not present in the default SDK.
37For example, Metal Performance Shaders were added in macOS 12.
38If the default SDK is 11.3, then a package that requires Metal Performance Shaders will fail to build due to missing frameworks and symbols.
39
40To use a non-default SDK, add it to your derivation’s `buildInputs`.
41It is not necessary to override the SDK in the `stdenv` nor is it necessary to override the SDK used by your dependencies.
42If your derivation needs a non-default SDK at build time (e.g., for a `depsBuildBuild` compiler), see the cross-compilation documentation for which input you should use.
43
44When determining whether to use a non-default SDK, consider the following:
45
46- Try building your derivation with the default SDK. If it works, you’re done.
47- If the package specifies a specific version, use that. See below for how to map Xcode version to SDK version.
48- If the package’s documentation indicates it supports optional features on newer SDKs, consider using the SDK that enables those features.
49 If you’re not sure, use the default SDK.
50
51Note: It is possible to have multiple, different SDK versions in your inputs.
52When that happens, the one with the highest version is always used.
53
54```nix
55stdenv.mkDerivation {
56 name = "libfoo-1.2.3";
57 # ...
58 buildInputs = [ apple-sdk_14 ];
59}
60```
61
62#### What is a “deployment target” (or minimum version)? {#sec-darwin-troubleshooting-using-deployment-targets}
63
64The “deployment target” refers to the minimum version of macOS that is expected to run an application.
65In most cases, the default is fine, and you don’t have to do anything else.
66If you’re not sure, don’t do anything, and that will probably be fine.
67
68Some packages require setting a non-default deployment target (or minimum version) to gain access to certain APIs.
69You do that using the `darwinMinVersionHook`, which takes the deployment target version as a parameter.
70There are primarily two ways to determine the deployment target.
71
72- The upstream documentation will specify a deployment target or minimum version. Use that.
73- The build will fail because an API requires a certain version. Use that.
74- In all other cases, you probably don’t need to specify a minimum version. The default is usually good enough.
75
76```nix
77stdenv.mkDerivation {
78 name = "libfoo-1.2.3"; # Upstream specifies the minimum supported version as 12.5.
79 buildInputs = [ (darwinMinVersionHook "12.5") ];
80}
81```
82
83Note: It is possible to have multiple, different instances of `darwinMinVersionHook` in your inputs.
84When that happens, the one with the highest version is always used.
85
86#### Picking an SDK version {#sec-darwin-troubleshooting-picking-sdk-version}
87
88The following is a list of Xcode versions, the SDK version in Nixpkgs, and the attribute to use to add it.
89Check your package’s documentation (platform support or installation instructions) to find which Xcode or SDK version to use.
90Generally, only the last SDK release for a major version is packaged.
91
92| Xcode version | SDK version | Nixpkgs attribute |
93|--------------------|--------------------|------------------------------|
94| 12.0–12.5.1 | 11.3 | `apple-sdk_11` / `apple-sdk` |
95| 13.0–13.4.1 | 12.3 | `apple-sdk_12` |
96| 14.0–14.3.1 | 13.3 | `apple-sdk_13` |
97| 15.0–15.4 | 14.4 | `apple-sdk_14` |
98| 16.0 | 15.0 | `apple-sdk_15` |
99
100
101#### Darwin Default SDK versions {#sec-darwin-troubleshooting-darwin-defaults}
102
103The current default version of the SDK and deployment target (minimum supported version) are indicated by the Darwin-specific platform attributes `darwinSdkVersion` and `darwinMinVersion`.
104Because of the ways that minimum version and SDK can be changed that are not visible to Nix, they should be treated as lower bounds.
105If you need to parameterize over a specific version, create a function that takes the version as a parameter instead of relying on these attributes.
106
107On macOS, the `darwinMinVersion` and `darwinSdkVersion` are always the same, and are currently set to 11.3.
108
109
110#### `xcrun` cannot find a binary {#sec-darwin-troubleshooting-xcrun}
111
112`xcrun` searches `PATH` and the SDK’s toolchain for binaries to run.
113If it cannot find a required binary, it will fail. When that happens, add the package for that binary to your derivation’s `nativeBuildInputs` (or `nativeCheckInputs` if the failure is happening when running tests).
114
115```nix
116stdenv.mkDerivation {
117 name = "libfoo-1.2.3";
118 # ...
119 nativeBuildInputs = [ bison ];
120 buildCommand = ''
121 xcrun bison foo.y # produces foo.tab.c
122 # ...
123 '';
124}
125```
126
127#### Package requires `xcodebuild` {#sec-darwin-troubleshooting-xcodebuild}
128
129The xcbuild package provides an `xcodebuild` command for packages that really depend on Xcode.
130This replacement is not 100% compatible and may run into some issues, but it is able to build many packages.
131To use `xcodebuild`, add `xcbuildHook` to your package’s `nativeBuildInputs`.
132It will provide a `buildPhase` for your derivation.
133You can use `xcbuildFlags` to specify flags to `xcodebuild` such as the required schema.
134If a schema has spaces in its name, you must set `__structuredAttrs` to `true`.
135See MoltenVK for an example of setting up xcbuild.
136
137```nix
138stdenv.mkDerivation {
139 name = "libfoo-1.2.3";
140 xcbuildFlags = [
141 "-configuration"
142 "Release"
143 "-project"
144 "libfoo-project.xcodeproj"
145 "-scheme"
146 "libfoo Package (macOS only)"
147 ];
148 __structuredAttrs = true;
149}
150```
151
152##### Fixing absolute paths to `xcodebuild`, `xcrun`, and `PlistBuddy` {#sec-darwin-troubleshooting-xcodebuild-absolute-paths}
153
154Many build systems hardcode the absolute paths to `xcodebuild`, `xcrun`, and `PlistBuddy` as `/usr/bin/xcodebuild`, `/usr/bin/xcrun`, and `/usr/libexec/PlistBuddy` respectively.
155These paths will need to be replaced with relative paths and the xcbuild package if `xcodebuild` or `PListBuddy` are used.
156
157```nix
158stdenv.mkDerivation {
159 name = "libfoo-1.2.3";
160 postPatch = ''
161 substituteInPlace Makefile \
162 --replace-fail '/usr/bin/xcodebuild' 'xcodebuild' \
163 --replace-fail '/usr/bin/xcrun' 'xcrun' \
164 --replace-fail '/usr/bin/PListBuddy' 'PListBuddy'
165 '';
166}
167```
168
169#### How to use libiconv on Darwin {#sec-darwin-troubleshooting-libiconv}
170
171The libiconv package is included in the SDK by default along with libresolv and libsbuf.
172You do not need to do anything to use these packages. They are available automatically.
173If your derivation needs the `iconv` binary, add the `libiconv` package to your `nativeBuildInputs` (or `nativeCheckInputs` for tests).
174
175#### Library install name issues {#sec-darwin-troubleshooting-install-name}
176
177Libraries on Darwin are usually linked with absolute paths.
178This is determined by something called an “install name”, which is resolved at link time.
179Sometimes packages will not set this correctly, causing binaries linking to it not to find their libraries at runtime.
180This can be fixed by adding extra linker flags or by using `install_name_tool` to set it in `fixupPhase`.
181
182##### Setting the install name via linker flags {#sec-darwin-troubleshooting-install-name-linker-flags}
183
184```nix
185stdenv.mkDerivation {
186 name = "libfoo-1.2.3";
187 # ...
188 makeFlags = lib.optional stdenv.hostPlatform.isDarwin "LDFLAGS=-Wl,-install_name,$(out)/lib/libfoo.dylib";
189}
190```
191
192##### Setting the install name using `install_name_tool` {#sec-darwin-troubleshooting-install-name-install_name_tool}
193
194```nix
195stdenv.mkDerivation {
196 name = "libfoo-1.2.3";
197 # ...
198 postFixup = ''
199 # `-id <install_name>` takes the install name. The last parameter is the path to the library.
200 ${stdenv.cc.targetPrefix}install_name_tool -id "$out/lib/libfoo.dylib" "$out/lib/libfoo.dylib"
201 '';
202}
203```
204
205Even if libraries are linked using absolute paths and resolved via their install name correctly, tests in `checkPhase` can sometimes fail to run binaries because they are linked against libraries that have not yet been installed.
206This can usually be solved by running the tests after the `installPhase` or by using `DYLD_LIBRARY_PATH` (see {manpage}`dyld(1)` for more on setting `DYLD_LIBRARY_PATH`).
207
208##### Setting the install name using `fixDarwinDylibNames` hook {#sec-darwin-troubleshooting-install-name-fixDarwinDylibNames}
209
210If your package has numerous dylibs needing fixed, while it is preferable to fix the issue in the package’s build, you can update them all by adding the `fixDarwinDylibNames` hook to your `nativeBuildInputs`.
211This hook will scan your package’s outputs for dylibs and correct their install names.
212Note that if any binaries in your outputs linked those dylibs, you may need to use `install_name_tool` to replace references to them with the correct paths.
213
214#### Propagating an SDK (advanced, compilers-only) {#sec-darwin-troubleshooting-propagating-sdks}
215
216The SDK is a package, and it can be propagated.
217`darwinMinVersionHook` with a version specified can also be propagated.
218However, most packages should *not* do this.
219The exception is compilers.
220When you propagate an SDK, it becomes part of your derivation’s public API, and changing the SDK or removing it can be a breaking change.
221That is why propagating it is only recommended for compilers.
222
223When authoring a compiler derivation, propagate the SDK only for the ways you expect users to use your compiler.
224Depending on your expected use cases, you may have to do one or all of these.
225
226- Put it in `depsTargetTargetPropagated` when your compiler is expected to be added to `nativeBuildInputs`.
227 That will ensure the SDK is effectively part of the target derivation’s `buildInputs`.
228- If your compiler uses a hook, put it in the hook’s `depsTargetTargetPropagated` instead.
229 The effect should be the same as the above.
230- If your package uses the builder pattern, update your builder to add the SDK to the derivation’s `buildInputs`.
231
232If you’re not sure whether to propagate an SDK, don’t.
233If your package is a compiler or language, and you’re not sure, ask @NixOS/darwin-maintainers for help deciding.
234
235### Dealing with `darwin.apple_sdk.frameworks` {#sec-darwin-legacy-frameworks}
236
237You may see references to `darwin.apple_sdk.frameworks`.
238This is the legacy SDK pattern, and it is being phased out.
239All packages in `darwin.apple_sdk`, `darwin.apple_sdk_11_0`, and `darwin.apple_sdk_12_3` are stubs that do nothing.
240If your derivation references them, you can delete them. The default SDK should be enough to build your package.
241
242Note: the new SDK pattern uses the name `apple-sdk` to better align with Nixpkgs naming conventions.
243The legacy SDK pattern uses `apple_sdk`.
244You always know you are using the old SDK pattern if the name is `apple_sdk`.
245
246Some derivations may depend on the location of frameworks in those old packages.
247To update your derivation to find them in the new SDK, use `$SDKROOT` instead in `preConfigure`.
248For example, if you substitute `${darwin.apple_sdk.frameworks.OpenGL}/Library/Frameworks/OpenGL.framework` in `postPatch`, replace it with `$SDKROOT/System/Library/Frameworks/OpenGL.framework` in `preConfigure`.
249
250Note that if your derivation is changing a system path (such as `/System/Library/Frameworks/OpenGL.framework`), you may be able to remove the path.
251Compilers and binutils targeting Darwin look for system paths in the SDK sysroot.
252Some of them (such as Zig or `bindgen` for Rust) depend on it.
253
254#### Updating legacy SDK overrides {#sec-darwin-legacy-frameworks-overrides}
255
256The legacy SDK provided two ways of overriding the default SDK.
257These are both being phased out along with the legacy SDKs.
258They have been updated to set up the new SDK for you, but you should replace them with doing that directly.
259
260- `pkgs.darwin.apple_sdk_11_0.callPackage` - this pattern was used to provide frameworks from the macOS 11 SDK.
261 It is now the same as `callPackage`.
262- `overrideSDK` - this stdenv adapter would try to replace the frameworks used by your derivation and its transitive dependencies.
263 It now adds the `apple-sdk_12` package for `12.3` and does nothing for `11.0`.
264 If `darwinMinVersion` is specified, it will add `darwinMinVersionHook` with the specified minimum version.
265 No other SDK versions are supported.
266
267### Darwin Cross-Compilation {#sec-darwin-legacy-cross-compilation}
268
269Darwin supports cross-compilation between Darwin platforms.
270Cross-compilation from Linux is not currently supported but may be supported in the future.
271To cross-compile to Darwin, you can set `crossSystem` or use one of the Darwin systems in `pkgsCross`.
272The `darwinMinVersionHook` and the SDKs support cross-compilation.
273If you need to specify a different SDK version for a `depsBuildBuild` compiler, add it to your `nativeBuildInputs`.
274
275```nix
276stdenv.mkDerivation {
277 name = "libfoo-1.2.3";
278 # ...
279 depsBuildBuild = [ buildPackages.stdenv.cc ];
280 nativeBuildInputs = [ apple-sdk_12 ];
281 buildInputs = [ apple-sdk_13 ];
282 depsTargetTargetPropagated = [ apple-sdk_14 ];
283}
284# The build-build `clang` will use the 12.3 SDK while the package build itself will use the 13.3 SDK.
285# Derivations that add this package as an input will have the 14.4 SDK propagated to them.
286```
287
288The different target SDK and hooks are mangled based on role:
289
290- `DEVELOPER_DIR_FOR_BUILD` and `MACOSX_DEPLOYMENT_TARGET_FOR_BUILD` for the build platform;
291- `DEVELOPER_DIR` and `MACOSX_DEPLOYMENT_TARGET` for the host platform; and
292- `DEVELOPER_DIR_FOR_TARGET` and `MACOSX_DEPLOYMENT_TARGET_FOR_TARGET` for the build platform.
293
294In static compilation situations, it is possible for the build and host platform to be the same platform but have different SDKs with the same version (one dynamic and one static).
295cc-wrapper and bintools-wrapper take care of handling this distinction.