1# Swift {#swift}
2
3The Swift compiler is provided by the `swift` package:
4
5```sh
6# Compile and link a simple executable.
7nix-shell -p swift --run 'swiftc -' <<< 'print("Hello world!")'
8# Run it!
9./main
10```
11
12The `swift` package also provides the `swift` command, with some caveats:
13
14- Swift Package Manager (SwiftPM) is packaged separately as `swiftpm`. If you
15 need functionality like `swift build`, `swift run`, `swift test`, you must
16 also add the `swiftpm` package to your closure.
17- On Darwin, the `swift repl` command requires an Xcode installation. This is
18 because it uses the system LLDB debugserver, which has special entitlements.
19
20## Module search paths {#ssec-swift-module-search-paths}
21
22Like other toolchains in Nixpkgs, the Swift compiler executables are wrapped
23to help Swift find your application's dependencies in the Nix store. These
24wrappers scan the `buildInputs` of your package derivation for specific
25directories where Swift modules are placed by convention, and automatically
26add those directories to the Swift compiler search paths.
27
28Swift follows different conventions depending on the platform. The wrappers
29look for the following directories:
30
31- On Darwin platforms: `lib/swift/macosx`
32 (If not targeting macOS, replace `macosx` with the Xcode platform name.)
33- On other platforms: `lib/swift/linux/x86_64`
34 (Where `linux` and `x86_64` are from lowercase `uname -sm`.)
35- For convenience, Nixpkgs also adds `lib/swift` to the search path.
36 This can save a bit of work packaging Swift modules, because many Nix builds
37 will produce output for just one target any way.
38
39## Core libraries {#ssec-swift-core-libraries}
40
41In addition to the standard library, the Swift toolchain contains some
42additional 'core libraries' that, on Apple platforms, are normally distributed
43as part of the OS or Xcode. These are packaged separately in Nixpkgs, and can
44be found (for use in `buildInputs`) as:
45
46- `swiftPackages.Dispatch`
47- `swiftPackages.Foundation`
48- `swiftPackages.XCTest`
49
50## Packaging with SwiftPM {#ssec-swift-packaging-with-swiftpm}
51
52Nixpkgs includes a small helper `swiftpm2nix` that can fetch your SwiftPM
53dependencies for you, when you need to write a Nix expression to package your
54application.
55
56The first step is to run the generator:
57
58```sh
59cd /path/to/my/project
60# Enter a Nix shell with the required tools.
61nix-shell -p swift swiftpm swiftpm2nix
62# First, make sure the workspace is up-to-date.
63swift package resolve
64# Now generate the Nix code.
65swiftpm2nix
66```
67
68This produces some files in a directory `nix`, which will be part of your Nix
69expression. The next step is to write that expression:
70
71```nix
72{
73 stdenv,
74 swift,
75 swiftpm,
76 swiftpm2nix,
77 fetchFromGitHub,
78}:
79
80let
81 # Pass the generated files to the helper.
82 generated = swiftpm2nix.helpers ./nix;
83in
84
85stdenv.mkDerivation (finalAttrs: {
86 pname = "myproject";
87 version = "0.0.0";
88
89 src = fetchFromGitHub {
90 owner = "nixos";
91 repo = "myproject";
92 tag = finalAttrs.version;
93 hash = "";
94 };
95
96 # Including SwiftPM as a nativeBuildInput provides a buildPhase for you.
97 # This by default performs a release build using SwiftPM, essentially:
98 # swift build -c release
99 nativeBuildInputs = [
100 swift
101 swiftpm
102 ];
103
104 # The helper provides a configure snippet that will prepare all dependencies
105 # in the correct place, where SwiftPM expects them.
106 configurePhase = ''
107 runHook preConfigure
108
109 ${generated.configure}
110
111 runHook postConfigure
112 '';
113
114 installPhase = ''
115 runHook preInstall
116
117 # This is a special function that invokes swiftpm to find the location
118 # of the binaries it produced.
119 binPath="$(swiftpmBinPath)"
120 # Now perform any installation steps.
121 mkdir -p $out/bin
122 cp $binPath/myproject $out/bin/
123
124 runHook postInstall
125 '';
126})
127```
128
129### Custom build flags {#ssec-swiftpm-custom-build-flags}
130
131If you'd like to build a different configuration than `release`:
132
133```nix
134{
135 swiftpmBuildConfig = "debug";
136}
137```
138
139It is also possible to provide additional flags to `swift build`:
140
141```nix
142{
143 swiftpmFlags = [ "--disable-dead-strip" ];
144}
145```
146
147The default `buildPhase` already passes `-j` for parallel building.
148
149If these two customization options are insufficient, provide your own
150`buildPhase` that invokes `swift build`.
151
152### Running tests {#ssec-swiftpm-running-tests}
153
154Including `swiftpm` in your `nativeBuildInputs` also provides a default
155`checkPhase`, but it must be enabled with:
156
157```nix
158{
159 doCheck = true;
160}
161```
162
163This essentially runs: `swift test -c release`
164
165### Patching dependencies {#ssec-swiftpm-patching-dependencies}
166
167In some cases, it may be necessary to patch a SwiftPM dependency. SwiftPM
168dependencies are located in `.build/checkouts`, but the `swiftpm2nix` helper
169provides these as symlinks to read-only `/nix/store` paths. In order to patch
170them, we need to make them writable.
171
172A special function `swiftpmMakeMutable` is available to replace the symlink
173with a writable copy:
174
175```nix
176{
177 configurePhase = ''
178 runHook preConfigure
179
180 ${generated.configure}
181
182 # Replace the dependency symlink with a writable copy.
183 swiftpmMakeMutable swift-crypto
184 # Now apply a patch.
185 patch -p1 -d .build/checkouts/swift-crypto -i ${./some-fix.patch}
186
187 runHook postConfigure
188 '';
189}
190```
191
192## Considerations for custom build tools {#ssec-swift-considerations-for-custom-build-tools}
193
194### Linking the standard library {#ssec-swift-linking-the-standard-library}
195
196The `swift` package has a separate `lib` output containing just the Swift
197standard library, to prevent Swift applications needing a dependency on the
198full Swift compiler at run-time. Linking with the Nixpkgs Swift toolchain
199already ensures binaries correctly reference the `lib` output.
200
201Sometimes, Swift is used only to compile part of a mixed codebase, and the
202link step is manual. Custom build tools often locate the standard library
203relative to the `swift` compiler executable, and while the result will work,
204when this path ends up in the binary, it will have the Swift compiler as an
205unintended dependency.
206
207In this case, you should investigate how your build process discovers the
208standard library, and override the path. The correct path will be something
209like: `"${swift.swift.lib}/${swift.swiftModuleSubdir}"`