{ lib , stdenv , callPackage , cmake , ninja , git , swift , swiftpm2nix , Foundation , XCTest , sqlite , ncurses , substituteAll , runCommandLocal , makeWrapper , DarwinTools # sw_vers , cctools # vtool , xcbuild , CryptoKit , LocalAuthentication }: let inherit (swift) swiftOs swiftModuleSubdir swiftStaticModuleSubdir; sharedLibraryExt = stdenv.hostPlatform.extensions.sharedLibrary; sources = callPackage ../sources.nix { }; generated = swiftpm2nix.helpers ./generated; cmakeGlue = callPackage ./cmake-glue.nix { }; # Common attributes for the bootstrap swiftpm and the final swiftpm. commonAttrs = { inherit (sources) version; src = sources.swift-package-manager; nativeBuildInputs = [ makeWrapper ]; # Required at run-time for the host platform to build package manifests. propagatedBuildInputs = [ Foundation ]; patches = [ ./patches/cmake-disable-rpath.patch ./patches/disable-index-store.patch ./patches/disable-sandbox.patch ./patches/fix-clang-cxx.patch (substituteAll { src = ./patches/disable-xctest.patch; inherit (builtins) storeDir; }) (substituteAll { src = ./patches/fix-stdlib-path.patch; inherit (builtins) storeDir; swiftLib = swift.swift.lib; }) ]; postPatch = '' # The location of xcrun is hardcoded. We need PATH lookup instead. find Sources -name '*.swift' | xargs sed -i -e 's|/usr/bin/xcrun|xcrun|g' # Patch the location where swiftpm looks for its API modules. substituteInPlace Sources/PackageModel/UserToolchain.swift \ --replace \ 'librariesPath = applicationPath.parentDirectory' \ "librariesPath = AbsolutePath(\"$out\")" ''; }; # Tools invoked by swiftpm at run-time. runtimeDeps = [ git ] ++ lib.optionals stdenv.isDarwin [ xcbuild.xcrun # vtool is used to determine a minimum deployment target. This is part of # cctools, but adding that as a build input puts an unwrapped linker in # PATH, and breaks builds. This small derivation exposes just vtool. (runCommandLocal "vtool" { } '' mkdir -p $out/bin ln -s ${cctools}/bin/vtool $out/bin/vtool '') ]; # Common attributes for the bootstrap derivations. mkBootstrapDerivation = attrs: stdenv.mkDerivation (attrs // { nativeBuildInputs = (attrs.nativeBuildInputs or [ ]) ++ [ cmake ninja swift ] ++ lib.optionals stdenv.isDarwin [ DarwinTools ]; buildInputs = (attrs.buildInputs or [ ]) ++ [ Foundation ]; postPatch = (attrs.postPatch or "") + lib.optionalString stdenv.isDarwin '' # On Darwin only, Swift uses arm64 as cpu arch. if [ -e cmake/modules/SwiftSupport.cmake ]; then substituteInPlace cmake/modules/SwiftSupport.cmake \ --replace '"aarch64" PARENT_SCOPE' '"arm64" PARENT_SCOPE' fi ''; preConfigure = (attrs.preConfigure or "") + '' # Builds often don't set a target, and our default minimum macOS deployment # target on x86_64-darwin is too low. Harmless on non-Darwin. export MACOSX_DEPLOYMENT_TARGET=10.15.4 ''; postInstall = (attrs.postInstall or "") + lib.optionalString stdenv.isDarwin '' # The install name of libraries is incorrectly set to lib/ (via our # CMake setup hook) instead of lib/swift/. This'd be easily fixed by # fixDarwinDylibNames, but some builds create libraries that reference # eachother, and we also have to fix those references. dylibs="$(find $out/lib/swift* -name '*.dylib')" changes="" for dylib in $dylibs; do changes+=" -change $(otool -D $dylib | tail -n 1) $dylib" done for dylib in $dylibs; do install_name_tool -id $dylib $changes $dylib done ''; cmakeFlags = (attrs.cmakeFlags or [ ]) ++ [ # Some builds link to libraries within the same build. Make sure these # create references to $out. None of our builds run their own products, # so we don't have to account for that scenario. "-DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON" ]; }); # On Darwin, we only want ncurses in the linker search path, because headers # are part of libsystem. Adding its headers to the search path causes strange # mixing and errors. # TODO: Find a better way to prevent this conflict. ncursesInput = if stdenv.isDarwin then ncurses.out else ncurses; # Derivations for bootstrapping dependencies using CMake. # This is based on the `swiftpm/Utilities/bootstrap` script. # # Some of the installation steps here are a bit hacky, because it seems like # these packages were not really meant to be installed using CMake. The # regular swiftpm bootstrap simply refers to the source and build # directories. The advantage of separate builds is that we can more easily # link libs together using existing Nixpkgs infra. # # In the end, we don't expose these derivations, and they only exist during # the bootstrap phase. The final swiftpm derivation does not depend on them. swift-system = mkBootstrapDerivation { name = "swift-system"; src = generated.sources.swift-system; postInstall = cmakeGlue.SwiftSystem + lib.optionalString (!stdenv.isDarwin) '' # The cmake rules apparently only use the Darwin install convention. # Fix up the installation so the module can be found on non-Darwin. mkdir -p $out/${swiftStaticModuleSubdir} mv $out/lib/swift_static/${swiftOs}/*.swiftmodule $out/${swiftStaticModuleSubdir}/ ''; }; swift-collections = mkBootstrapDerivation { name = "swift-collections"; src = generated.sources.swift-collections; postPatch = '' # Only builds static libs on Linux, but this installation difference is a # hassle. Because this installation is temporary for the bootstrap, may # as well build static libs everywhere. sed -i -e '/BUILD_SHARED_LIBS/d' CMakeLists.txt ''; postInstall = cmakeGlue.SwiftCollections + lib.optionalString (!stdenv.isDarwin) '' # The cmake rules apparently only use the Darwin install convention. # Fix up the installation so the module can be found on non-Darwin. mkdir -p $out/${swiftStaticModuleSubdir} mv $out/lib/swift_static/${swiftOs}/*.swiftmodule $out/${swiftStaticModuleSubdir}/ ''; }; swift-tools-support-core = mkBootstrapDerivation { name = "swift-tools-support-core"; src = generated.sources.swift-tools-support-core; buildInputs = [ swift-system sqlite ]; postInstall = cmakeGlue.TSC + '' # Swift modules are not installed. mkdir -p $out/${swiftModuleSubdir} cp swift/*.swift{module,doc} $out/${swiftModuleSubdir}/ # Static libs are not installed. cp lib/*.a $out/lib/ # Headers are not installed. mkdir -p $out/include cp -r ../Sources/TSCclibc/include $out/include/TSC ''; }; swift-argument-parser = mkBootstrapDerivation { name = "swift-argument-parser"; src = generated.sources.swift-argument-parser; buildInputs = [ ncursesInput sqlite ]; cmakeFlags = [ "-DBUILD_TESTING=NO" "-DBUILD_EXAMPLES=NO" ]; postInstall = cmakeGlue.ArgumentParser + lib.optionalString stdenv.isLinux '' # Fix rpath so ArgumentParserToolInfo can be found. patchelf --add-rpath "$out/lib/swift/${swiftOs}" \ $out/lib/swift/${swiftOs}/libArgumentParser.so ''; }; Yams = mkBootstrapDerivation { name = "Yams"; src = generated.sources.Yams; # Conflicts with BUILD file on case-insensitive filesystems. cmakeBuildDir = "_build"; postInstall = cmakeGlue.Yams; }; llbuild = mkBootstrapDerivation { name = "llbuild"; src = generated.sources.swift-llbuild; nativeBuildInputs = lib.optional stdenv.isDarwin xcbuild; buildInputs = [ ncursesInput sqlite ]; patches = [ ./patches/llbuild-cmake-disable-rpath.patch ]; postPatch = '' # Substitute ncurses for curses. find . -name CMakeLists.txt | xargs sed -i -e 's/curses/ncurses/' # Use absolute install names instead of rpath. substituteInPlace \ products/libllbuild/CMakeLists.txt \ products/llbuildSwift/CMakeLists.txt \ --replace '@rpath' "$out/lib" # This subdirectory is enabled for Darwin only, but requires ObjC XCTest # (and only Swift XCTest is open source). substituteInPlace perftests/CMakeLists.txt \ --replace 'add_subdirectory(Xcode/' '#add_subdirectory(Xcode/' ''; cmakeFlags = [ "-DLLBUILD_SUPPORT_BINDINGS=Swift" ]; postInstall = cmakeGlue.LLBuild + '' # Install module map. cp ../products/libllbuild/include/module.modulemap $out/include # Swift modules are not installed. mkdir -p $out/${swiftModuleSubdir} cp products/llbuildSwift/*.swift{module,doc} $out/${swiftModuleSubdir}/ ''; }; swift-driver = mkBootstrapDerivation { name = "swift-driver"; src = generated.sources.swift-driver; buildInputs = [ Yams llbuild swift-system swift-argument-parser swift-tools-support-core ]; postInstall = cmakeGlue.SwiftDriver + '' # Swift modules are not installed. mkdir -p $out/${swiftModuleSubdir} cp swift/*.swift{module,doc} $out/${swiftModuleSubdir}/ ''; }; swift-crypto = mkBootstrapDerivation { name = "swift-crypto"; src = generated.sources.swift-crypto; postPatch = '' substituteInPlace CMakeLists.txt \ --replace /usr/bin/ar $NIX_CC/bin/ar ''; postInstall = cmakeGlue.SwiftCrypto + '' # Static libs are not installed. cp lib/*.a $out/lib/ # Headers are not installed. cp -r ../Sources/CCryptoBoringSSL/include $out/include ''; }; # Build a bootrapping swiftpm using CMake. swiftpm-bootstrap = mkBootstrapDerivation (commonAttrs // { pname = "swiftpm-bootstrap"; buildInputs = [ llbuild swift-argument-parser swift-collections swift-crypto swift-driver swift-system swift-tools-support-core ]; cmakeFlags = [ "-DUSE_CMAKE_INSTALL=ON" ]; postInstall = '' for program in $out/bin/swift-*; do wrapProgram $program --prefix PATH : ${lib.makeBinPath runtimeDeps} done ''; }); # Build the final swiftpm with the bootstrapping swiftpm. in stdenv.mkDerivation (commonAttrs // { pname = "swiftpm"; nativeBuildInputs = commonAttrs.nativeBuildInputs ++ [ swift swiftpm-bootstrap ]; buildInputs = [ ncursesInput sqlite XCTest ] ++ lib.optionals stdenv.isDarwin [ CryptoKit LocalAuthentication ]; configurePhase = generated.configure + '' # Functionality provided by Xcode XCTest, but not available in # swift-corelibs-xctest. swiftpmMakeMutable swift-tools-support-core substituteInPlace .build/checkouts/swift-tools-support-core/Sources/TSCTestSupport/XCTestCasePerf.swift \ --replace 'canImport(Darwin)' 'false' # Prevent a warning about SDK directories we don't have. swiftpmMakeMutable swift-driver patch -p1 -d .build/checkouts/swift-driver -i ${substituteAll { src = ../swift-driver/patches/prevent-sdk-dirs-warnings.patch; inherit (builtins) storeDir; }} ''; buildPhase = '' # Required to link with swift-corelibs-xctest on Darwin. export SWIFTTSC_MACOS_DEPLOYMENT_TARGET=10.12 TERM=dumb swift-build -c release ''; # TODO: Tests depend on indexstore-db being provided by an existing Swift # toolchain. (ie. looks for `../lib/libIndexStore.so` relative to swiftc. #doCheck = true; #checkPhase = '' # TERM=dumb swift-test -c release #''; # The following is dervied from Utilities/bootstrap, see install_swiftpm. installPhase = '' binPath="$(swift-build --show-bin-path -c release)" mkdir -p $out/bin $out/lib/swift cp $binPath/swift-package $out/bin/ wrapProgram $out/bin/swift-package \ --prefix PATH : ${lib.makeBinPath runtimeDeps} for tool in swift-build swift-test swift-run swift-package-collection; do ln -s $out/bin/swift-package $out/bin/$tool done installSwiftpmModule() { mkdir -p $out/lib/swift/pm/$2 cp $binPath/lib$1${sharedLibraryExt} $out/lib/swift/pm/$2/ if [[ -f $binPath/$1.swiftinterface ]]; then cp $binPath/$1.swiftinterface $out/lib/swift/pm/$2/ else cp -r $binPath/$1.swiftmodule $out/lib/swift/pm/$2/ fi cp $binPath/$1.swiftdoc $out/lib/swift/pm/$2/ } installSwiftpmModule PackageDescription ManifestAPI installSwiftpmModule PackagePlugin PluginAPI ''; setupHook = ./setup-hook.sh; meta = { description = "The Package Manager for the Swift Programming Language"; homepage = "https://github.com/apple/swift-package-manager"; platforms = with lib.platforms; linux ++ darwin; license = lib.licenses.asl20; maintainers = with lib.maintainers; [ dtzWill trepetti dduan trundle stephank ]; }; })