Debugging Swift on Fedora Linux

If you'd like to hack on Apple's Swift Programming Language on Fedora, for, say, troubleshooting purposes or whatever, here are the steps I've been using:

Set up the environment and build a release version

First, I recommend using a container; I would share mine but I haven't been able to figure out how to do that with podman.

Basically you want to start with a fedora image in the usual way, but it's super important you pass --privileged to it like:

podman run -it --privileged fedora:34 /bin/bash

The container has to be set up to build RPMs; you need to install a bunch of stuff like so:

dnf install -y fedora-packager fedora-review vim git

Then using git, clone my repo:

git clone https://github.com/tachoknight/swift-lang-packaging-fedora.git

and check out the previous commit:

git checkout e3f2

that will put you at the right commit; there's a Swift 5.5-DEVELOPMENT version that I started working on as well.

Run the "justbuild.sh" script; it should get all the dependencies needed to build Swift. Depending on what kind of machine you're running, prepare for about three hours to build everything at minimum.

Time passes 😴


Installing Swift

Assuming everything is built okay, you should be able to run:

dnf install -y ~/RPMS/x86_64/swift-lang-5.4-1.fc34.x86_64.rpm

This will install Swift install /usr/libexec/swift. In that directory you'll see Swift's own directory structure (e.g. bin, lib). Great, Swift is installed but in release mode.

NOW we get to the debug part. The whole purpose of doing the stuff above was to get everything in place so we can start fiddling with the source without having to deal with patch files. Plus it also guarantees that everything is working before it gets all screwed up (for me anyway 🙃).

First we need to get rid of the existing binaries:

cd ~/rpmbuild/BUILD
rm -rf usr

Now we're going to set up Swift for debugging. Type:

cd ~/rpmbuild/BUILD/swift-source

Then type:

rm -rf binforpython

This removes some stuff that the spec file creates due to some historical issues around Python 3 and Swift still wanting Python 2. Anyhoo, now type:

vim ./swift/utils/build-presets.ini

On line 818, under the section [preset: buildbot_linux], you should see "release" on its own line. Change that to "debug". Save and exit the file.

Now type
cd ~/rpmbuild/SPECS

And here is where we're going to rebuild Swift in debug mode. It's very important this line be entered exactly as entered:

rpmbuild -bc --short-circuit ./swift-lang.spec

What the command above does is to simply recompile the code without re-downloading anything. It's important that this be the only command to use when rebuilding Swift because rpmbuild wants to scrub the ~/rpmbuild/BUILD directory clean so you'd lose any changes.

W A R N I N G

You thought it took a long time to build Swift in release mode? Double it. Maybe even triple it. Kick this job off before you go to bed; it will likely still be going in the morning (or evening, depending on your schedule).


Using the debug version of Swift

The new rpm has the debug version of everything, but I don't really bother doing that and instead simply copy the contents of ~/rpmnbuild/BUILD/usr into something like /usr/libexec/swift-debug, renaming the existing /usr/libexec/swift to /usr/libexec/swift-release and then create a symlink to the one you want to use (I like to keep the release version around as a santiy check).

Running swift in the debugger

If you want to run a particular swift tool, it's easy to run it in the debugger, which is included with the toolchain as /usr/libexec/swift/bin/lldb. So if you want to run the actual swift program in the debugger, type:

/usr/libexec/swift/bin/lldb /usr/libexec/swift/bin/swift

When building a package that requires swift build, that actually invokes the program /usr/libexec/swift/bin/swift-build. So debugging the "swift build" command would be:

/usr/libexec/swift/bin/lldb /usr/libexec/swift/bin/swift-build