How To: Compile Swift on a Raspberry Pi 2

Raspberry Pi 2s Hard at Work Update 2016/2/9: As of February 2016 it’s a little bit simpler to build Swift on a Raspberry Pi 2. For details, see my updated post: Compiling Swift on a Raspberry Pi 2 (February 2016 Update) and a Script to Clone and Build Open Source Swift

Time Required: 10 - 12 hours, mostly unattended

If you’re more interested in actually using Swift on your Raspberry Pi than just compiling it, you can download precompiled packages by William Dillon at House Dillon or Joe Bell at iAchieved.it. Mr. Dillion is hpux735 on GitHub, and it is thanks to excellent work by him, tienex, and others that we are able to compile Swift on the Raspberry Pi 2 so shortly after it was open sourced.

As of early January 2016 many things are still broken. For example, at the moment the Swift REPL crashes on the Raspberry Pi 2 with a memory corruption error when you try to do pretty much anything. Until just a few days ago Foundation was similarly broken. That said, many simple programs can already be compiled with swiftc, and things are moving forward extremely quickly. YMMV.

Requirements:

  • 1 x Raspberry Pi 2
  • 1 x 8 GB or larger microSD card1

I recommend starting with Raspbian Jessie Lite. See my post on how to prep a Raspberry Pi 2 for compiling swift for my 20 minute set up routine.

(The “time required” line for each step is just how long it took on my Pis—you may get wildly different results.)

Step 1: Install Dependencies

Start by installing the dependencies described in the Swift README:

sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config

(Time required: 2m40s)

On Raspbian Jessie Lite, all dependencies should resolve without any issues.

Step 2: Make a Giant Swap File

Compiling a release build of Swift on the Raspberry Pi 2 uses about 1.3 GB of swap space.2 I recommend setting up a 2 GB swap file.3
In /etc/dphys-swapfile change CONF_SWAPSIZE to 2048 and restart:

sudo vim /etc/dphys-swapfile
sudo reboot

Given the extra time it takes to create a 2 GB swap file it will probably take around two minutes for your Pi to restart after this change.

Step 3: Set Up a Directory Structure and Clone the Repos

Create a directory to hold the Swift sources:

mkdir ~/apple

…and a subdirectory to hold the output of the compile and install process:

mkdir -p ~/apple/dest

Everything from here on happens in the directory with the Swift sources:

cd ~/apple

As mentioned above, it’s thanks to the work of William Dillon, tienex, and others that we can compile Swift on ARM today. Hence, until their pull requests have been merged into the official sources, the key to building Swift on ARM is to simply clone some of their forks of the official Swift repositories. Below we substitute Mr. Dillon’s forks of Swift, LLVM, LLDB, and Foundation in place of the official repositories:

git clone https://github.com/hpux735/swift.git swift
git clone https://github.com/hpux735/swift-llvm.git llvm
git clone https://github.com/apple/swift-clang.git clang
git clone https://github.com/hpux735/swift-lldb.git lldb
git clone https://github.com/apple/swift-cmark.git cmark
git clone https://github.com/apple/swift-llbuild.git llbuild
git clone https://github.com/apple/swift-package-manager.git swiftpm
git clone https://github.com/apple/swift-corelibs-xctest.git
git clone https://github.com/hpux735/swift-corelibs-foundation.git

(Time required: 12m11s)

Step 4: Start the Build

Start by launching screen so that the build keeps running if (when) we disconnect from SSH:

screen -S buildswift

The build script for Swift takes a ton of options, and the script it calls, build-script-impl, takes even more. Here are three build commands I’ve used to more or less success:

Option 1: Short and Simple

swift/utils/build-script -R

(Time required: 7h52m53s)

As of January 6 this dumps a lot of warnings, but it does get as far as giving us a swiftc that we can use to compile simple code below ~/apple/build/Ninja-ReleaseAssert.

Option 2: ARM Buildbot

swift/utils/build-script --preset=buildbot_linux_armv7 install_destdir=$HOME/apple/dest installable_package=$HOME/apple/swift-$(date +%F).tar.gz

(Time required: 9h25m9s)

Mr. Dillon has helpfully included a build preset that works on ARM. This command installs Swift into ~/apple/dest and gives us a nice dated package in ~/apple.

Option 3: Massive Command Line

swift/utils/build-script --no-assertions --no-swift-stdlib-assertions --llbuild --xctest --lldb --release --foundation -- --swift-enable-ast-verifier=0 --install-swift --install-lldb --install-llbuild --install-xctest --install-foundation --install-prefix=/usr '--swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;dev' --build-swift-static-stdlib=1 --skip-test-lldb=1 --install-destdir=$HOME/apple/dest --installable-package=$HOME/apple/swift-$(date +%F).tar.gz --reconfigure

(Time required: 6h47m16s)

As described in build-script --help, the preferred method for creating custom build presets is to define them in ~/.swift-build-presets and specify them with --preset=.

It’s not as pretty, but I actually prefer a massive command line.

The above command is based off the buildbot_linux preset:

  • Like Mr. Dillon’s buildbot_linux_armv7 it skips tests (many fail) and doesn’t attempt to build the Swift Package Manager (which doesn’t build at the moment).
  • Assertions are rejected at the suggestion that this produces faster binaries.
  • I’ve included Foundation, even though it isn’t really function on ARM at this point. Leaving off --foundation and --install-foudation shaves nearly an hour off the build time.
  • Swift is installed into ~/apple/dest, and a date-stamped package is saved in ~/apple.

(Aside) Using Screen

  • You can detach from your screen session with Ctrl-a d.
  • You can reconnect to your screen session with screen -r buildswift.
  • You can see what screen sessions are running with screen -ls

(Aside) Monitoring the Build

A good way to monitor the status of the system during the build is with vmstat. After detaching from screen, open a new screen session:

screen -S vmstats

Let’s set vmstat to run every 10 seconds in the background and save the output to a dated file:

vmstat 10 >> swift-build-vmstats-$(date +%F) &

You can then follow along with tail:

tail -f swift-build-vmstats-*

When the build is done you can stop vmstat either with kill or by typing fg and pressing Ctrl-C.
After the build is finished it’s easy to use awk to do things like find out the maximum amount of swap space used:

awk '$3 ~ /^[0-9]*$/ {if ($3>max) max=$3} END {print max}' swift-build-2015-12-30

(Aside) Monitoring Pi’s CPU Temperature

Along with the statistics reported by vmstat, you can also monitor the CPU temperature on the Pi over the course of the build:

while cat /sys/class/thermal/thermal_zone0/temp >> cputemp; do sleep 10; done &

As with output from vmstat, we can use tail -f to follow along in realtime, and awk to analyze the data at the end (in the above script, replace $3 with $1). In the course of a typical compile my Pi spent 5.5 hours above 60° C and reached 65.369° C for less than a minute.

Step 5 (Optional): Copy the Installation to a Different Location

Two of the build commands above install Swift into ~/apple/dest. Although it’s not an optimal workflow for a production system, for testing purposes on a temporary machine4 you can just run sudo cp -r ~/apple/dest/usr / . Similarly, you can “install” the tarball by unpacking it at the top of the filesystem with sudo tar -xvf swift*.tar.gz -C / . (To use this tarball on another Raspbian system note that you will also have to install Clang with sudo apt-get install clang .)

Where to Next?

Swift on a Raspberry Pi 2

Swift on ARM is advancing rapidly. Indeed, in the course of writing this post I’ve seen a number of new bugs filed on the Swift bug tracker, a number of new pull requests submitted on GitHub, and active discussion on the Swift mailing lists. I’m excited to see where things will go next.


  1. Building Swift on an 8 GB microSD card freshly burned with Raspbian Jessie Lite left me with 996 MB free. A larger card is probably preferable. [return]
  2. The primary memory bottleneck occurs during the linking stage—for much of the compilation process the swap space should remain pretty much untouched. One of the reasons for choosing to go with a release build is that linking a debug build requires much, much more memory. [return]
  3. Given multiple swap files set to equal priorities, the Linux kernel writes out memory pages across them round-robin style. Unfortunately, on the Raspberry Pi 2 this doesn’t end up being significantly faster than using a single swap file on the main microSD card. Pretty much everything on the Pi—the internal microSD card reader, the 4 USB ports, the 100Mbps Ethernet port—is connected to the CPU via a single 480Mbps USB 2 bus. Accounting for protocol overhead, in practice this means that there’s somewhere between 20 and 30 MB/sec of total IO bandwidth on the system, regardless of the number and speed of the drives or network adapters you connect. [return]
  4. One that can be easily rebuilt. [return]