1{
2 lib,
3 stdenv,
4 callPackage,
5 fetchpatch,
6 cmake,
7 ninja,
8 git,
9 swift,
10 swiftpm2nix,
11 Foundation,
12 XCTest,
13 pkg-config,
14 sqlite,
15 ncurses,
16 replaceVars,
17 runCommandLocal,
18 makeWrapper,
19 DarwinTools, # sw_vers
20 cctools, # vtool
21 xcbuild,
22}:
23
24let
25
26 inherit (swift) swiftOs swiftModuleSubdir swiftStaticModuleSubdir;
27 sharedLibraryExt = stdenv.hostPlatform.extensions.sharedLibrary;
28
29 sources = callPackage ../sources.nix { };
30 generated = swiftpm2nix.helpers ./generated;
31 cmakeGlue = callPackage ./cmake-glue.nix { };
32
33 # Common attributes for the bootstrap swiftpm and the final swiftpm.
34 commonAttrs = {
35 inherit (sources) version;
36 src = sources.swift-package-manager;
37 nativeBuildInputs = [ makeWrapper ];
38 # Required at run-time for the host platform to build package manifests.
39 propagatedBuildInputs = [ Foundation ];
40 patches = [
41 ./patches/cmake-disable-rpath.patch
42 ./patches/cmake-fix-quoting.patch
43 ./patches/disable-index-store.patch
44 ./patches/disable-sandbox.patch
45 ./patches/disable-xctest.patch
46 ./patches/fix-clang-cxx.patch
47 ./patches/nix-pkgconfig-vars.patch
48 (replaceVars ./patches/fix-stdlib-path.patch {
49 inherit (builtins) storeDir;
50 swiftLib = swift.swift.lib;
51 })
52 ];
53 postPatch = ''
54 # The location of xcrun is hardcoded. We need PATH lookup instead.
55 find Sources -name '*.swift' | xargs sed -i -e 's|/usr/bin/xcrun|xcrun|g'
56
57 # Patch the location where swiftpm looks for its API modules.
58 substituteInPlace Sources/PackageModel/UserToolchain.swift \
59 --replace \
60 'librariesPath = applicationPath.parentDirectory' \
61 "librariesPath = AbsolutePath(\"$out\")"
62
63 # Fix case-sensitivity issues.
64 # Upstream PR: https://github.com/apple/swift-package-manager/pull/6500
65 substituteInPlace Sources/CMakeLists.txt \
66 --replace \
67 'packageCollectionsSigning' \
68 'PackageCollectionsSigning'
69 substituteInPlace Sources/PackageCollectionsSigning/CMakeLists.txt \
70 --replace \
71 'SubjectPublickeyInfo' \
72 'SubjectPublicKeyInfo'
73 substituteInPlace Sources/PackageCollections/CMakeLists.txt \
74 --replace \
75 'FilepackageCollectionsSourcesStorage' \
76 'FilePackageCollectionsSourcesStorage'
77 '';
78 };
79
80 # Tools invoked by swiftpm at run-time.
81 runtimeDeps = [
82 git
83 ]
84 ++ lib.optionals stdenv.hostPlatform.isDarwin [
85 xcbuild.xcrun
86 # These tools are part of cctools, but adding that as a build input puts
87 # an unwrapped linker in PATH, and breaks builds. This small derivation
88 # exposes just the tools we need:
89 # - vtool is used to determine a minimum deployment target.
90 # - libtool is used to build static libraries.
91 (runCommandLocal "swiftpm-cctools" { } ''
92 mkdir -p $out/bin
93 ln -s ${cctools}/bin/vtool $out/bin/vtool
94 ln -s ${cctools}/bin/libtool $out/bin/libtool
95 '')
96 ];
97
98 # Common attributes for the bootstrap derivations.
99 mkBootstrapDerivation =
100 attrs:
101 stdenv.mkDerivation (
102 attrs
103 // {
104 nativeBuildInputs =
105 (attrs.nativeBuildInputs or [ ])
106 ++ [
107 cmake
108 ninja
109 swift
110 ]
111 ++ lib.optionals stdenv.hostPlatform.isDarwin [ DarwinTools ];
112
113 buildInputs = (attrs.buildInputs or [ ]) ++ [ Foundation ];
114
115 postPatch =
116 (attrs.postPatch or "")
117 + lib.optionalString stdenv.hostPlatform.isDarwin ''
118 # On Darwin only, Swift uses arm64 as cpu arch.
119 if [ -e cmake/modules/SwiftSupport.cmake ]; then
120 substituteInPlace cmake/modules/SwiftSupport.cmake \
121 --replace '"aarch64" PARENT_SCOPE' '"arm64" PARENT_SCOPE'
122 fi
123 '';
124
125 postInstall =
126 (attrs.postInstall or "")
127 + lib.optionalString stdenv.hostPlatform.isDarwin ''
128 # The install name of libraries is incorrectly set to lib/ (via our
129 # CMake setup hook) instead of lib/swift/. This'd be easily fixed by
130 # fixDarwinDylibNames, but some builds create libraries that reference
131 # eachother, and we also have to fix those references.
132 dylibs="$(find $out/lib/swift* -name '*.dylib')"
133 changes=""
134 for dylib in $dylibs; do
135 changes+=" -change $(otool -D $dylib | tail -n 1) $dylib"
136 done
137 for dylib in $dylibs; do
138 install_name_tool -id $dylib $changes $dylib
139 done
140 '';
141
142 cmakeFlags = (attrs.cmakeFlags or [ ]) ++ [
143 # Some builds link to libraries within the same build. Make sure these
144 # create references to $out. None of our builds run their own products,
145 # so we don't have to account for that scenario.
146 "-DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON"
147 ];
148 }
149 );
150
151 # On Darwin, we only want ncurses in the linker search path, because headers
152 # are part of libsystem. Adding its headers to the search path causes strange
153 # mixing and errors.
154 # TODO: Find a better way to prevent this conflict.
155 ncursesInput = if stdenv.hostPlatform.isDarwin then ncurses.out else ncurses;
156
157 # Derivations for bootstrapping dependencies using CMake.
158 # This is based on the `swiftpm/Utilities/bootstrap` script.
159 #
160 # Some of the installation steps here are a bit hacky, because it seems like
161 # these packages were not really meant to be installed using CMake. The
162 # regular swiftpm bootstrap simply refers to the source and build
163 # directories. The advantage of separate builds is that we can more easily
164 # link libs together using existing Nixpkgs infra.
165 #
166 # In the end, we don't expose these derivations, and they only exist during
167 # the bootstrap phase. The final swiftpm derivation does not depend on them.
168
169 swift-system = mkBootstrapDerivation {
170 name = "swift-system";
171 src = generated.sources.swift-system;
172
173 postInstall =
174 cmakeGlue.SwiftSystem
175 + lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
176 # The cmake rules apparently only use the Darwin install convention.
177 # Fix up the installation so the module can be found on non-Darwin.
178 mkdir -p $out/${swiftStaticModuleSubdir}
179 mv $out/lib/swift_static/${swiftOs}/*.swiftmodule $out/${swiftStaticModuleSubdir}/
180 '';
181 };
182
183 swift-collections = mkBootstrapDerivation {
184 name = "swift-collections";
185 src = generated.sources.swift-collections;
186
187 postPatch = ''
188 # Only builds static libs on Linux, but this installation difference is a
189 # hassle. Because this installation is temporary for the bootstrap, may
190 # as well build static libs everywhere.
191 sed -i -e '/BUILD_SHARED_LIBS/d' CMakeLists.txt
192 '';
193
194 postInstall =
195 cmakeGlue.SwiftCollections
196 + lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
197 # The cmake rules apparently only use the Darwin install convention.
198 # Fix up the installation so the module can be found on non-Darwin.
199 mkdir -p $out/${swiftStaticModuleSubdir}
200 mv $out/lib/swift_static/${swiftOs}/*.swiftmodule $out/${swiftStaticModuleSubdir}/
201 '';
202 };
203
204 # Part of this patch fixes for glibc 2.39: glibc patch 64b1a44183a3094672ed304532bedb9acc707554
205 # marks the `FILE*` argument to a few functions including `ferror` & `fread` as non-null. However
206 # the code passes an `Optional<T>` to these functions.
207 # This patch uses a `guard` which effectively unwraps the type (or throws an exception).
208 swift-tools-support-core-glibc-fix = fetchpatch {
209 url = "https://github.com/apple/swift-tools-support-core/commit/990afca47e75cce136d2f59e464577e68a164035.patch";
210 hash = "sha256-PLzWsp+syiUBHhEFS8+WyUcSae5p0Lhk7SSRdNvfouE=";
211 includes = [ "Sources/TSCBasic/FileSystem.swift" ];
212 };
213
214 swift-tools-support-core = mkBootstrapDerivation {
215 name = "swift-tools-support-core";
216 src = generated.sources.swift-tools-support-core;
217
218 patches = [
219 swift-tools-support-core-glibc-fix
220 ];
221
222 buildInputs = [
223 swift-system
224 sqlite
225 ];
226
227 postInstall = cmakeGlue.TSC + ''
228 # Swift modules are not installed.
229 mkdir -p $out/${swiftModuleSubdir}
230 cp swift/*.swift{module,doc} $out/${swiftModuleSubdir}/
231
232 # Static libs are not installed.
233 cp lib/*.a $out/lib/
234
235 # Headers are not installed.
236 mkdir -p $out/include
237 cp -r ../Sources/TSCclibc/include $out/include/TSC
238 '';
239 };
240
241 swift-argument-parser = mkBootstrapDerivation {
242 name = "swift-argument-parser";
243 src = generated.sources.swift-argument-parser;
244
245 buildInputs = [
246 ncursesInput
247 sqlite
248 ];
249
250 cmakeFlags = [
251 "-DBUILD_TESTING=NO"
252 "-DBUILD_EXAMPLES=NO"
253 ];
254
255 postInstall =
256 cmakeGlue.ArgumentParser
257 + lib.optionalString stdenv.hostPlatform.isLinux ''
258 # Fix rpath so ArgumentParserToolInfo can be found.
259 patchelf --add-rpath "$out/lib/swift/${swiftOs}" \
260 $out/lib/swift/${swiftOs}/libArgumentParser.so
261 '';
262 };
263
264 Yams = mkBootstrapDerivation {
265 name = "Yams";
266 src = generated.sources.Yams;
267
268 # Conflicts with BUILD file on case-insensitive filesystems.
269 cmakeBuildDir = "_build";
270
271 postInstall = cmakeGlue.Yams;
272 };
273
274 llbuild = mkBootstrapDerivation {
275 name = "llbuild";
276 src = generated.sources.swift-llbuild;
277
278 nativeBuildInputs = lib.optional stdenv.hostPlatform.isDarwin xcbuild;
279 buildInputs = [
280 ncursesInput
281 sqlite
282 ];
283
284 patches = [
285 ./patches/llbuild-cmake-disable-rpath.patch
286 ];
287
288 postPatch = ''
289 # Substitute ncurses for curses.
290 find . -name CMakeLists.txt | xargs sed -i -e 's/curses/ncurses/'
291
292 # Use absolute install names instead of rpath.
293 substituteInPlace \
294 products/libllbuild/CMakeLists.txt \
295 products/llbuildSwift/CMakeLists.txt \
296 --replace '@rpath' "$out/lib"
297
298 # This subdirectory is enabled for Darwin only, but requires ObjC XCTest
299 # (and only Swift XCTest is open source).
300 substituteInPlace perftests/CMakeLists.txt \
301 --replace 'add_subdirectory(Xcode/' '#add_subdirectory(Xcode/'
302 '';
303
304 cmakeFlags = [
305 "-DLLBUILD_SUPPORT_BINDINGS=Swift"
306 ];
307
308 postInstall = cmakeGlue.LLBuild + ''
309 # Install module map.
310 cp ../products/libllbuild/include/module.modulemap $out/include
311
312 # Swift modules are not installed.
313 mkdir -p $out/${swiftModuleSubdir}
314 cp products/llbuildSwift/*.swift{module,doc} $out/${swiftModuleSubdir}/
315 '';
316 };
317
318 swift-driver = mkBootstrapDerivation {
319 name = "swift-driver";
320 src = generated.sources.swift-driver;
321
322 buildInputs = [
323 Yams
324 llbuild
325 swift-system
326 swift-argument-parser
327 swift-tools-support-core
328 ];
329
330 postPatch = ''
331 # Tries to link against CYaml, but that's private.
332 substituteInPlace Sources/SwiftDriver/CMakeLists.txt \
333 --replace CYaml ""
334 '';
335
336 postInstall = cmakeGlue.SwiftDriver + ''
337 # Swift modules are not installed.
338 mkdir -p $out/${swiftModuleSubdir}
339 cp swift/*.swift{module,doc} $out/${swiftModuleSubdir}/
340 '';
341 };
342
343 swift-crypto = mkBootstrapDerivation {
344 name = "swift-crypto";
345 src = generated.sources.swift-crypto;
346
347 postPatch = ''
348 # Fix use of hardcoded tool paths on Darwin.
349 substituteInPlace CMakeLists.txt \
350 --replace /usr/bin/ar $NIX_CC/bin/ar
351 substituteInPlace CMakeLists.txt \
352 --replace /usr/bin/ranlib $NIX_CC/bin/ranlib
353 '';
354
355 postInstall = cmakeGlue.SwiftCrypto + ''
356 # Static libs are not installed.
357 cp lib/*.a $out/lib/
358
359 # Headers are not installed.
360 cp -r ../Sources/CCryptoBoringSSL/include $out/include
361 '';
362 };
363
364 # Build a bootrapping swiftpm using CMake.
365 swiftpm-bootstrap = mkBootstrapDerivation (
366 commonAttrs
367 // {
368 pname = "swiftpm-bootstrap";
369
370 buildInputs = [
371 llbuild
372 sqlite
373 swift-argument-parser
374 swift-collections
375 swift-crypto
376 swift-driver
377 swift-system
378 swift-tools-support-core
379 ];
380
381 cmakeFlags = [
382 "-DUSE_CMAKE_INSTALL=ON"
383 ];
384
385 postInstall = ''
386 for program in $out/bin/swift-*; do
387 wrapProgram $program --prefix PATH : ${lib.makeBinPath runtimeDeps}
388 done
389 '';
390 }
391 );
392
393in
394# Build the final swiftpm with the bootstrapping swiftpm.
395stdenv.mkDerivation (
396 commonAttrs
397 // {
398 pname = "swiftpm";
399
400 nativeBuildInputs = commonAttrs.nativeBuildInputs ++ [
401 pkg-config
402 swift
403 swiftpm-bootstrap
404 ];
405 buildInputs = [
406 ncursesInput
407 sqlite
408 XCTest
409 ];
410
411 configurePhase = generated.configure + ''
412 # Functionality provided by Xcode XCTest, but not available in
413 # swift-corelibs-xctest.
414 swiftpmMakeMutable swift-tools-support-core
415 substituteInPlace .build/checkouts/swift-tools-support-core/Sources/TSCTestSupport/XCTestCasePerf.swift \
416 --replace 'canImport(Darwin)' 'false'
417 patch -p1 -d .build/checkouts/swift-tools-support-core -i ${swift-tools-support-core-glibc-fix}
418
419 # Prevent a warning about SDK directories we don't have.
420 swiftpmMakeMutable swift-driver
421 patch -p1 -d .build/checkouts/swift-driver -i ${
422 replaceVars ../swift-driver/patches/prevent-sdk-dirs-warnings.patch {
423 inherit (builtins) storeDir;
424 }
425 }
426 '';
427
428 buildPhase = ''
429 TERM=dumb swift-build -c release
430 '';
431
432 # TODO: Tests depend on indexstore-db being provided by an existing Swift
433 # toolchain. (ie. looks for `../lib/libIndexStore.so` relative to swiftc.
434 #doCheck = true;
435 #checkPhase = ''
436 # TERM=dumb swift-test -c release
437 #'';
438
439 # The following is derived from Utilities/bootstrap, see install_swiftpm.
440 installPhase = ''
441 binPath="$(swift-build --show-bin-path -c release)"
442
443 mkdir -p $out/bin $out/lib/swift
444
445 cp $binPath/swift-package-manager $out/bin/swift-package
446 wrapProgram $out/bin/swift-package \
447 --prefix PATH : ${lib.makeBinPath runtimeDeps}
448 for tool in swift-build swift-test swift-run swift-package-collection swift-experimental-destination; do
449 ln -s $out/bin/swift-package $out/bin/$tool
450 done
451
452 installSwiftpmModule() {
453 mkdir -p $out/lib/swift/pm/$2
454 cp $binPath/lib$1${sharedLibraryExt} $out/lib/swift/pm/$2/
455
456 if [[ -f $binPath/$1.swiftinterface ]]; then
457 cp $binPath/$1.swiftinterface $out/lib/swift/pm/$2/
458 else
459 cp -r $binPath/$1.swiftmodule $out/lib/swift/pm/$2/
460 fi
461 cp $binPath/$1.swiftdoc $out/lib/swift/pm/$2/
462 }
463 installSwiftpmModule PackageDescription ManifestAPI
464 installSwiftpmModule PackagePlugin PluginAPI
465 '';
466
467 setupHook = ./setup-hook.sh;
468
469 meta = {
470 description = "Package Manager for the Swift Programming Language";
471 homepage = "https://github.com/apple/swift-package-manager";
472 platforms = with lib.platforms; linux ++ darwin;
473 license = lib.licenses.asl20;
474 teams = [ lib.teams.swift ];
475 };
476 }
477)