I spent the last few hours leisurely getting a Swift toolchain installed on my Mac to allow me to cross-compile code for a Raspberry Pi 4 I picked up last week. The RPi is not my favorite embedded computer (I prefer the Beaglebone Black), but I wanted to give it a try, and there’s more support out there for the Raspberry Pi than the BBB.

The community has made excellent strides and put in a lot of effort to get the tools up and running, but like so many open-source projects, documentation is lacking. You can’t skip over many of the steps required to build the tools from scratch, even though they provide and recommend you use the pre-built binaries. Hopefully this will explain how to get everything installed.

Note that they recommend using Docker. I barely understand Docker, and have avoided it so far. I think it lets you run an Arm/Linux environment on the Mac to test your builds. I’m sure that will be convenient, but I haven’t tried it.

There are two basic things that need to be done: install the cross-compiler on macOS, and install the runtime on raspbian.

Installing the Cross Compiler

I’m running Buster Lite on a Raspberry Pi 4 B. Altough the RPi 4 is a 64-bit capable computer, Raspbian is still (annoyingly, 32-bit only). You’ll need the armv6 version of the toolchain, available from https://github.com/CSCIX65G/SwiftCrossCompilers/releases.

I downloaded the Swift-armv7-5.0.pkg package, but make sure there’s not a more recent version there. Note: the resulting tools should be named with armv6 in the name, but they’re actually armhf. Hopefully that’s fixed soon.

Double-click the resulting .pkg file and let macOS install the pieces.

Installing the Runtime

My RPi 4 is running Raspbian Buster Lite from 2019-07-10.

The easiest way to get the runtime installed on the target machine is to grab a prebuilt toolchain from https://github.com/uraimo/buildSwiftOnARM#prebuilt-binaries. I went with the https://github.com/uraimo/buildSwiftOnARM/releases/download/5.0.2/swift-5.0.2-armv7-DebianBuster.tgz build. Again, check to make sure there’s not a more recent version. It’s probably important that the runtime version you install roughly match the compiler you installed on the Mac above (e.g. v5.0.x, v5.1.x, etc). I don't know how well the recent Swift ABI stability really works.

To install the toolchain, follow these steps:

$ wget https://github.com/uraimo/buildSwiftOnARM/releases/download/5.0.2/swift-5.0.2-armv7-DebianBuster.tgz
$ tar -zxf swift-5.0.2-armv7-DebianBuster.tgz

I install the toolchain under /opt/swift using a directory structure that lets me keep several versions around. There may be more conventional ways to handle this.

$ sudo mkdir -p /opt/swift/versions/swift-5.0.2-armv7-DebianBuster
$ sudo mv usr /opt/swift/versions/swift-5.0.2-armv7-DebianBuster
$ sudo ln -s /opt/swift/versions/swift-5.0.2-armv7-DebianBuster /opt/swift/current

When that’s done you should have this:

$ ls -l /opt/swift/
total 4
lrwxrwxrwx 1 root root   50 Aug 25 07:25 current -> /opt/swift/versions/swift-5.0.2-armv7-DebianBuster
drwxr-xr-x 3 root root 4096 Aug 25 07:25 versions

Update your PATH with (you might want to add this to the end of .bashrc or similar):

$ export PATH="/opt/swift/current:$PATH"

Helping Raspbian Find the Runtime

Another important step is to make sure the dynamic loader can find the Swift runtime libraries.

$ sudo su
# cat > /etc/ld.so.conf.d/swift.conf <<EOF
/opt/swift/current/lib/swift/linux
/opt/swift/current/lib/swift/clang/lib/linux
/opt/swift/current/lib/swift/pm
EOF
# ldconfig
# exit

Testing

At this point you should be able to get the Swift compiler version:

$ swift --version
Swift version 5.0.2 (swift-5.0.2-RELEASE)
Target: armv7-unknown-linux-gnueabihf

Hello World

Now you can try their Hello World example. On the Mac, create a directory and use the Swift Package Manager to create a skeleton project. Note that these steps differ a bit from what’s written in the readme at the SwiftCrossCompilers repo:

$ mkdir helloworld && cd helloworld
$ swift package init --type=executable
$ swift build --destination /Library/Developer/Destinations/armhf-5.0-RELEASE.json
$ file .build/debug/helloworld
.build/debug/helloworld: ELF 32-bit LSB pie executable ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, with debug_info, not stripped

You should be able to copy this executable to your Raspberry Pi:

$ scp .build/debug/helloworld raspberrypi.local:
helloworld

Then, on the Raspberry Pi, try running it:

$ ssh raspberrypi.local
$ ./helloworld
Hello, world!

I hope I got all the necessary steps correct above. It took me some meandering and retracing my steps to get to this point. I’m publishing this article as much to remind me how I did it as to help others. Please let me know in the comments below if you run into any issues.

Thanks for reading!