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 = generated.configure; 107 108 installPhase = '' 109 runHook preInstall 110 111 # This is a special function that invokes swiftpm to find the location 112 # of the binaries it produced. 113 binPath="$(swiftpmBinPath)" 114 # Now perform any installation steps. 115 mkdir -p $out/bin 116 cp $binPath/myproject $out/bin/ 117 118 runHook postInstall 119 ''; 120}) 121``` 122 123### Custom build flags {#ssec-swiftpm-custom-build-flags} 124 125If you'd like to build a different configuration than `release`: 126 127```nix 128{ 129 swiftpmBuildConfig = "debug"; 130} 131``` 132 133It is also possible to provide additional flags to `swift build`: 134 135```nix 136{ 137 swiftpmFlags = [ "--disable-dead-strip" ]; 138} 139``` 140 141The default `buildPhase` already passes `-j` for parallel building. 142 143If these two customization options are insufficient, provide your own 144`buildPhase` that invokes `swift build`. 145 146### Running tests {#ssec-swiftpm-running-tests} 147 148Including `swiftpm` in your `nativeBuildInputs` also provides a default 149`checkPhase`, but it must be enabled with: 150 151```nix 152{ 153 doCheck = true; 154} 155``` 156 157This essentially runs: `swift test -c release` 158 159### Patching dependencies {#ssec-swiftpm-patching-dependencies} 160 161In some cases, it may be necessary to patch a SwiftPM dependency. SwiftPM 162dependencies are located in `.build/checkouts`, but the `swiftpm2nix` helper 163provides these as symlinks to read-only `/nix/store` paths. In order to patch 164them, we need to make them writable. 165 166A special function `swiftpmMakeMutable` is available to replace the symlink 167with a writable copy: 168 169```nix 170{ 171 configurePhase = generated.configure ++ '' 172 # Replace the dependency symlink with a writable copy. 173 swiftpmMakeMutable swift-crypto 174 # Now apply a patch. 175 patch -p1 -d .build/checkouts/swift-crypto -i ${./some-fix.patch} 176 ''; 177} 178``` 179 180## Considerations for custom build tools {#ssec-swift-considerations-for-custom-build-tools} 181 182### Linking the standard library {#ssec-swift-linking-the-standard-library} 183 184The `swift` package has a separate `lib` output containing just the Swift 185standard library, to prevent Swift applications needing a dependency on the 186full Swift compiler at run-time. Linking with the Nixpkgs Swift toolchain 187already ensures binaries correctly reference the `lib` output. 188 189Sometimes, Swift is used only to compile part of a mixed codebase, and the 190link step is manual. Custom build tools often locate the standard library 191relative to the `swift` compiler executable, and while the result will work, 192when this path ends up in the binary, it will have the Swift compiler as an 193unintended dependency. 194 195In this case, you should investigate how your build process discovers the 196standard library, and override the path. The correct path will be something 197like: `"${swift.swift.lib}/${swift.swiftModuleSubdir}"`