Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swift: split build & Darwin support. #189977

Merged
merged 27 commits into from Jan 17, 2023
Merged

Conversation

stephank
Copy link
Contributor

@stephank stephank commented Sep 6, 2022

Description of changes

This PR splits up the Swift build into smaller derivations, and brings up Darwin support for the toolchain. Fixes #37473.

I've kept the same maintainers listed as before, also for the new derivations that were already part of the toolchain. Hopefully that's okay, otherwise, let me know.

To perform the split, I've added a swiftPackages. It's not very large, but avoids cluttering the global namespace, and helps set up a scope with the required clang, macOS SDK, etc.

Tests are a pain point, and I've disabled them in many places. I believe we can fix many of the tests, but it'll take more effort.

Usage

The swift attribute now gets you just the Swift compiler (swift and swiftc). These tools are now wrapped, and a new setup-hook adds lib/swift of inputs to the Swift include path automatically. This is especially important on Darwin, where we separately package system frameworks.

The attribute swiftpm installs swift-build, swift-test, swift-run, though these are usually invoked without the hyphen by users (swift build). Additionally, a new setup-hook provides default build and check phases for convenience.

Build process

Previously, our Swift build used the Python-based 'build-script' to build Swift, which I believe matches official Swift CI. This new build bypasses the Python scripts and invokes CMake directly. I personally find this approach better for us, because:

  • We can split up the build. Smaller derivations = shorter iteration times and more granular caching. Especially for bringing up Darwin support, I've found quicker builds to be a great help.
  • I reached a point where the build-presets.ini patches were becoming hard to stack and maintain.
  • CMake scripts already have sane defaults for many options.
  • We can leverage existing Nix CMake smarts.

The basic compiler derivation now builds primarily CMark, Clang, and Swift. We also build LLDB, but only because the REPL uses it. On Darwin, there is also some extra work for back-deployment, though I've not actually tested if/how back-deployment works.

On top of this, I implemented a wrapper similar to cc-wrapper.

I've separately packaged Dispatch, Foundation and XCTest. Dispatch is based on an existing derivation, but Swift support was added.

The second complex build is for SwiftPM. We match the official build process here in first doing a CMake build, then using that to build SwiftPM itself. I wrote a small swiftpm2nix tool based on shell + jq, which actually replaces the bulk of the fetchgit source derivations from the old Swift build.

Beyond that, I also packaged the other parts of the SDK, but believe only sourcekit-lsp is currently useful.

Darwin stdenv

The biggest change to stdenv is that Swift modules are installed. These are os and Darwin for libsystem, and per-framework Swift overlays where available. I do have a small list of modules from the SDK that I couldn't place yet:

  • AppleArchive
  • Compression
  • DataDetection
  • ExtensionFoundation
  • ExtensionKit
  • QuickLookUI
  • ShazamKit
  • simd

I skipped these to limit time spent polishing details, and hope we can fix these as they become an issue.

Maybe it's possible to avoid these changes to existing SDK derivations by having separate derivations for all the Swift-specific stuff, but I haven't tried this, and wonder if it's worth it. Apple seems to treat Swift more and more as a core part of the system, and it'd make packaging a bit more tedious.

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 22.11 Release Notes (or backporting 22.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
    • (Release notes changes) Ran nixos/doc/manual/md-to-db.sh to update generated release notes
  • Fits CONTRIBUTING.md.

] ++ lib.optional stdenv.hostPlatform.isAarch32 ./armv7l.patch;

../../common/compiler-rt/darwin-plistbuddy-workaround.patch
./armv7l.patch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this patch now here unconditionally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -59,8 +60,9 @@ stdenv.mkDerivation {
# extra `/`.
./normalize-var.patch
../../common/compiler-rt/libsanitizer-no-cyclades-11.patch
] ++ lib.optional stdenv.hostPlatform.isAarch32 ./armv7l.patch;

../../common/compiler-rt/darwin-plistbuddy-workaround.patch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darwin-only?

++ lib.optional stdenv.hostPlatform.isDarwin ./darwin-targetconditionals.patch
++ lib.optional stdenv.hostPlatform.isAarch32 ./armv7l.patch;
# Prevent a compilation error on darwin
./darwin-targetconditionals.patch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also darwin-only.

Comment on lines +18 to +22
# 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a general problem on Darwin or only for swift packages?

Copy link
Contributor Author

@stephank stephank Sep 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be specific to the LLVM modules logic, because if it was broader, I feel like we would've encountered it elsewhere. If so, it's possible ObjC can also cause this (maybe even C? I believe the flag is -fmodules.)

Maybe we should eventually get rid of some of these headers in libsystem. It currently ships headers for ncurses, sqlite, krb5, maybe more, but we package those separately (and libsystem doesn't even provide implementations of those). A similar discussion came up in: #186251 (comment)

@stephank
Copy link
Contributor Author

stephank commented Sep 6, 2022

cc swift maintainers: @dtzWill @trepetti @dduan @Trundle
cc swift-corelibs-libdispatch maintainer: @cmm
cc for darwin stdenv: @toonn? Should we ping more?

@stephank stephank force-pushed the feat/swift-darwin branch 2 times, most recently from e7f08ca to 6a3c17e Compare September 8, 2022 17:57
@stephank
Copy link
Contributor Author

stephank commented Sep 8, 2022

While trying to build mpv, I noticed it invokes the compiler as swift -frontend, which was not wrapped. I modified the wrapper to also handle that specific case.

The build for mpv then fails with the same CoreVideo error as my little SwiftUI test app. Still debugging that issue.

@stephank
Copy link
Contributor Author

Some more tweaks:

  • The CoreVideo issue appears to be an actual SDK issue. Should've checked this earlier, but the haeder file on my 12.3 SDK does include stdint.h, while the 11.1 SDK we use doesn't. I've added a patch for it in ca85764.

  • SwiftPM now propagates Foundation as a native input. It uses Foundation on the host platform while building package manifests. (Even though the target platform might use a different Foundation. Haven't tested this scenario, really, it might pop up when targeting iOS.)

  • I moved around the swift -frontend wrapper fix, because it was in a part of the wrapper with a modified PATH. This caused the swift frontend to fail translating e.g. swift build to swift-build.

  • I replaced an external patch for LLVM I had included as a file with a fetchpatch invocation.

  • Added dependencies for the SwiftUI system framework.

I can now build a SwiftUI app! Will try mpv next, but I also rebased on current staging, and need to do a large rebuild.

@stephank
Copy link
Contributor Author

stephank commented Sep 10, 2022

  • mpv builds, with only small changes: stephank@e4566d2. Not part of this PR, think it's better to do it separately later? (EDIT: Also not sure what functionality to actually check at run-time. Never used mpv before myself.)
  • SwiftPM now propagates vtool as a native input. This was the last remaining tool that was causing prompts to install Command-Line Tools when using SwiftPM in a nix shell.
  • I've been testing the recent changes on aarch64-darwin, and have kicked off a build on x86_64-darwin to verify.

@toonn
Copy link
Contributor

toonn commented Sep 12, 2022

Re testing mpv, if it is not built with Swift it should spit out a warning:

[vo/gpu] opengl cocoa backend is deprecated, use vo=libmpv instead

It will also not be able to go into fullscreen mode when you press f (default shortcut).

This is probably not a thorough test of the functionality Swift adds to mpv but may be sufficient?

@Atemu
Copy link
Member

Atemu commented Sep 12, 2022

You're not actually using the new swift UI in that case. You need to add --vo=libmpv and then it works beautifully.

Building with "--disable-macos-cocoa-cb" makes it use libmpv by default but I'm not sure that's the best way of achieving that.

Copy link
Contributor

@toonn toonn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Darwin stdenv changes look OK to me. Though I'm not so familiar with aarch64-darwin, maybe @thefloweringash can take a look as well?

CoreVideo = lib.overrideDerivation super.CoreVideo (drv: {
installPhase = drv.installPhase + ''
# When used as a module, complains about a missing import for
# Darwin.C.stdint. Apparently fix in later SDKs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Darwin.C.stdint. Apparently fix in later SDKs.
# Darwin.C.stdint. Apparently fixed in later SDKs.

@toonn
Copy link
Contributor

toonn commented Sep 12, 2022

@Atemu, this is a way to test mpv is in fact using the Swift built libmpv. I'm saying if it was not then you'd get that error and fullscreen wouldn't work. This was just advice in response to stephank's edit:

(EDIT: Also not sure what functionality to actually check at run-time. Never used mpv before myself.)

@stephank
Copy link
Contributor Author

stephank commented Sep 13, 2022

Thanks for taking a look. Mpv fullscreen does work in the Swift enabled build (and not with my regular nix-darwin install, as expected), so that's a good sign.

For the REPL, it appears the logic is in swift-driver. The swift and swiftc executables forward calls to swift-driver if it exists. Current idea is to add swift-driver to the installation via the wrapper, and this works, but the new driver is not forwarding linker flags correctly. Once that's fixed, I also need to add the LLDB build somewhere

@tjni
Copy link
Contributor

tjni commented Sep 14, 2022

Would you mind if I extracted this commit and this commit into a separate PR to merge before this is ready? On my aarch64-darwin machine, they resolve building cargo-watch and xcbuild.

@stephank
Copy link
Contributor Author

Would you mind if I extracted this commit and this commit into a separate PR to merge before this is ready? On my aarch64-darwin machine, they resolve building cargo-watch and xcbuild.

Please do! I had no idea this was broader than Swift. 🙂

@stephank
Copy link
Contributor Author

I rebased and fixed a conflict with #181764. I don't believe that PR affects Darwin. On Linux, the fix from that PR doesn't appear to be necessary anymore.

Binaries for d63aef6 on x86_64-linux:

  • https://app.cachix.org/cache/stephank
  • Swift: /nix/store/7yk9wjj2zjbdm33qn2j2qpjvm7p1y0zd-swift-wrapper-5.7
  • SwiftPM: /nix/store/s976zxa2y0gh04hkgcdjkvs4h51927qa-swiftpm-5.7
  • SourceKit-LSP: /nix/store/1q8534vz6mr1p3xh4dhd6fk5zc2335n2-sourcekit-lsp-5.7

Copy link
Contributor

@toonn toonn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this work is considerable to say the least. Makes reviewing quite a task.
I'd like to ask to avoid further rebasing while reviews are on-going. Fixup commits are a good option for an autosquash rebase afterwards.

Thank you for the effort that must've gone into this! ❤️

Comment on lines +62 to +64
binPath="$(swiftpmBinPath)"
mkdir -p $out/bin
cp $binPath/sourcekit-lsp $out/bin/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
binPath="$(swiftpmBinPath)"
mkdir -p $out/bin
cp $binPath/sourcekit-lsp $out/bin/
mkdir -p $out/bin
cp "$(swiftpmBinPath)"/sourcekit-lsp $out/bin/

The intermediate variable doesn't really add anything IMO.

Where does swiftpmBinPath come from though, a hook? And do we want to use lib.getBin with it?

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 by easily fixed by
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# CMake setup hook) instead of lib/swift/. This'd by easily fixed by
# CMake setup hook) instead of lib/swift/. This'd be easily fixed by

''
+ lib.optionalString (!stdenv.isDarwin) ''
# The cmake rules apparently only use the Darwin install convention.
# Fix up the installation so to module can be found on non-Darwin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Fix up the installation so to module can be found on non-Darwin.
# Fix up the installation so the module can be found on non-Darwin.

''
+ lib.optionalString (!stdenv.isDarwin) ''
# The cmake rules apparently only use the Darwin install convention.
# Fix up the installation so to module can be found on non-Darwin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Fix up the installation so to module can be found on non-Darwin.
# Fix up the installation so the module can be found on non-Darwin.

sed -i -e '/BUILD_SHARED_LIBS/d' CMakeLists.txt
'';

postInstall = ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe refactor this out, since it's repeated?


preFixup = lib.optionalString stdenv.isLinux ''
# This is cheesy, but helps the patchelf hook remove /build from RPATH.
cd $NIX_BUILD_TOP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIX_BUILD_TOP should usually be avoided. I think it doesn't play nice with nix-shell for example. Is there an alternative way we can do this?

Copy link
Contributor Author

@stephank stephank Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I combined the glue files in a cmake-glue.nix, and it also combines these shell snippets needed for installation.

EDIT: This was supposed to be a comment on #189977 (comment)

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nix-macos-monthly/12330/34

@toonn toonn mentioned this pull request Nov 13, 2022
13 tasks
@stephank
Copy link
Contributor Author

I did a merge with a recent(ish) staging (when I started working on it again), and was able to verify a build on aarch64-darwin with these changes.

Swift has since released 5.7.1, but we can do that separately.

@ylh
Copy link
Contributor

ylh commented Dec 7, 2022

Reporting that this branch is of high enough quality that mpv, with some additional buildInputs tweaking, can now build with the Swift frontend instead of whatever --disable-macos-cocoa-cb gives us. This means nice things like better fullscreen support and the overlaid window decoration that vanishes along with the playback controls. Looking forward to this getting merged.

@ylh ylh mentioned this pull request Dec 7, 2022
13 tasks
@stephank
Copy link
Contributor Author

stephank commented Jan 2, 2023

Fwiw, I believe all feedback is addressed? Not sure what else I should do here. Would be great if we could move this forward!

@toonn
Copy link
Contributor

toonn commented Jan 2, 2023

I've been waiting for another pair of eyes on this. If that's not forthcoming we can merge though IMO.

@Atemu
Copy link
Member

Atemu commented Jan 3, 2023

Even if there is some issue you overlooked, I think this is a major improvement over the status quo with no obvious flaws. Any further refinements can always be done afterwards.

If you looked over it and everything you found has been addressed, I think it's good enough for staging/unstable.

@stephank
Copy link
Contributor Author

Ping! Can we merge this? 🙂

Copy link
Contributor

@toonn toonn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple remaining questions.

Copy link
Contributor

@toonn toonn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants