How To: Compile Swift on a Raspberry Pi 2
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 withCtrl-a d
. - You can reconnect to your
screen
session withscreen -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?
- Check out the source code samples in the IBM Swift Sandbox. Note which scripts work and which fail entirely.
- Foundation on Linux at iAchieved.it is a great introduction to the Foundation framework, one of the core libraries that was open-sourced along with Swift.
- Command Line Utilities in Swift is another fascinating article on iAchieved.it.
- And, of course, there’s the Swift Language Guide itself, which has plenty of simple code examples that work fine on ARM.
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.
- 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]
- 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]
- 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]
- One that can be easily rebuilt. [return]