Building Swift 3.0 on a Raspberry Pi 3

raspberry-pi-logo

Swift 3.0 Swift 3.0 Swift 3.0

9/24/2016 Update: See our latest post on building Swift 3.0 on ARMv7 systems such as the Pi 3.

Editor’s Note: This post was updated on 9/4/2016 to reflect the recent updates for Swift 3.0 (in particular it now includes the build of swift-corelibs-libdispatch).

A number of folks have expressed interest in building Swift 3.0 on their own Raspberry Pi 3. These instructions are for those brave souls who want to do so!

To get started you’re going to need:

Note that this can be done; we’ve done it, and there are group of folks dedicated to working with Swift on ARM-based devices. Don’t give up. If you do decide to give up we aren’t ones to judge, just head over and download our prebuilt Swift 3.0 package for the Raspberry Pi 2 and 3.

Install Xenial

We will not be using Raspbian here, but rather Ubuntu Xenial. Obtain Xenial with curl or wget and then write the filesystem out to your microSD card with dd.

Note: You’ve read this warning before I’m sure, but it is critical that you put in the right device name where we reference YOUR_DEVICE below. Failure to do so could destroy your data (they don’t call dd disk destroyer for nothing).

Using Linux

# wget http://www.finnie.org/software/raspberrypi/ubuntu-rpi3/ubuntu-16.04-preinstalled-server-armhf+raspi3.img.xz
# xzcat ubuntu-16.04-preinstalled-server-armhf+raspi3.img.xz | sudo dd of=/dev/YOUR_DEVICE bs=8M

To find your YOUR_DEVICE insert the microSD into your Linux machine and run dmesg|tail to see something like

[1255451.544316] usb-storage 4-2:1.0: USB Mass Storage device detected
[1255451.544466] scsi host25: usb-storage 4-2:1.0
[1255452.545332] scsi 25:0:0:0: Direct-Access     Generic  STORAGE DEVICE   0817 PQ: 0 ANSI: 6
[1255452.545670] sd 25:0:0:0: Attached scsi generic sg8 type 0
[1255452.828405] sd 25:0:0:0: [sdh] 15523840 512-byte logical blocks: (7.94 GB/7.40 GiB)
[1255452.829402] sd 25:0:0:0: [sdh] Write Protect is off
[1255452.829409] sd 25:0:0:0: [sdh] Mode Sense: 23 00 00 00
[1255452.830400] sd 25:0:0:0: [sdh] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[1255452.835967]  sdh: sdh1
[1255452.840401] sd 25:0:0:0: [sdh] Attached SCSI removable disk

In this example your device is /dev/sdh.

Using OS X (macOS)

I personally prefer to use a separate Linux server for these types of operations, but you can use a Mac as well with homebrew:

# brew install wget xz
# wget http://www.finnie.org/software/raspberrypi/ubuntu-rpi3/ubuntu-16.04-preinstalled-server-armhf+raspi3.img.xz
# xzcat ubuntu-16.04-preinstalled-server-armhf+raspi3.img.xz|sudo dd of=/dev/rdisk3 bs=8m

Note when using dd on the Mac its advisable to use /dev/rYOUR_DEVICE. Using /dev/YOUR_DEVICE can be painfully slow, as explained here.

To find YOUR_DEVICE on a Mac, insert the microSD and run diskutil list in a terminal. Look for the disk that that matches your SD card size (if you have other attached drives of the same size look also at its other attributes to ensure you are using the right one).

# diskutil list
...
/dev/disk5 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *16.0 GB    disk5
   1:             Windows_FAT_32 system-boot             134.2 MB   disk5s1
   2:                      Linux                         15.9 GB    disk5s2

If you are nervous about the steps with diskutil and dd, read this walkthrough to gain some confidence. As an aside, if you’re going to make a habit out of working with the Raspberry Pi the wget, xzcat, and dd commands are a critical part of your toolbox. Get comfortable with them.

Update Xenial

Now that you have Xenial written to the microSD card, insert it into the Raspberry Pi 3 and power up! And yes, we’re assuming you have an Ethernet cable attached or you have network connectivity through the miniUSB. You can try to ssh into the Pi with ssh ubuntu@ubuntu.local first, but if that fails use your router LAN page to determine the IP address. The default password is ubuntu.

# ssh ubuntu@192.168.1.115
The authenticity of host '192.168.1.115 (192.168.1.115)' can't be established.
ECDSA key fingerprint is 62:e9:f9:09:d0:30:3c:c9:0e:47:a3:42:f5:2c:e2:ae.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.115' (ECDSA) to the list of known hosts.
ubuntu@192.168.1.115's password:
You are required to change your password immediately (root enforced)
Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-1009-raspi2 armv7l)
...
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for ubuntu.
(current) UNIX password:

Once you’ve changed your password log back in with the ubuntu user (and your new password) and update the system with sudo apt-get update.

Warning: Previous versions of this post suggested running sudo apt-get upgrade -y as well. See the comments below for the workarounds if you cannot login via Ethernet afterwards.

Install Build Prerequisites

Compiling Swift requires a number of prerequisites. Get them in one fell swoop with:

# sudo apt-get install -y git cmake ninja-build clang uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config autoconf libtool systemtap-sdt-dev libcurl4-openssl-dev

Fortunately we didn’t have to discover the prerequisites iteratively (you know, when you download a repository and try to build it and discover what the prerequisites are, one at a time) because they are outlined in the Open Source Swift README.

I also like to include htop (sudo apt-get install -y htop) so I can see the Pi compile it’s little heart out:

100%
100%

Add Some Swap

While the Pi 3 does come with an impressive 1GB of RAM, that is still not enough to build Swift from beginning to end (it’s the linking steps that usually get us into trouble). Let’s add some swap to double that amount:

# cd /var/cache
# sudo mkdir swap
# cd swap
# sudo fallocate -l 1G 1G.swap
# sudo mkswap 1G.swap
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=184d002a-2f15-4b23-8360-8e792badc6a2
# sudo chmod 600 1G.swap
# sudo swapon 1G.swap

Here’s an example of what happens when you don’t have swap enabled:

cd /root/workspace/Swift-3.0-Pi3-ARM-Incremental/build/buildbot_linux/swift-linux-armv7/bin && /usr/bin/cmake -E create_symlink swift swiftc && cd /root/workspace/Swift-3.0-Pi3-ARM-Incremental/build/buildbot_linux/swift-linux-armv7/bin && /usr/bin/cmake -E create_symlink swift swift-autolink-extract
clang: error: unable to execute command: Killed
clang: error: linker command failed due to signal (use -v to see invocation)
ninja: build stopped: subcommand failed.
./swift/utils/build-script: fatal error: command terminated with a non-zero exit status 1, aborting

Doubling the amount of your Pi’s RAM with swap space that’s on a microSD card is not something you would typically do. Once we do hit the build phases that require us to dip into swap things will slow down, but the alternative is to crash with an out-of-memory error.

Use package-swift

package-swift is our Github repository that has several helper scripts to make life a little easier building Swift.

# git clone https://github.com/iachievedit/package-swift
# cd package-swift

By default package-swift will check out to the swift-3.0 branch. This branch contains important patches needed to properly build Swift for ARM (to see them take a look in patches/armv7, they are organized by the various repos they patch).

The scripts are pretty simple:

  • get.sh – Downloads all of the required repositories for building Swift
  • update.sh – Updates all of your local repositories with the latest on Github.
  • patch.sh – Applies any patches that the Swift on ARM team has put forward; from time to time we have to apply patches to get things working. Once a patch is no longer necessary we remove it, so before performing the actual build you will want to git pull to get the latest patchset
  • package.sh – Runs the Swift build-script with a preset named buildbot_linux_armv7.

I suggest you look at each shell script and walk through the logic before executing. They are straightforward but it’s worth the time understanding what they are doing. Pay particular attention to the get.sh and update.sh scripts in that they detect we are building on a ARMv7 processor and thus need William Dillon’s swift-llvm fork.

Let’s Do This

Remember to make sure you have:

  • installed all of the prerequisite packages
  • enabled 1GB of swap space
  • cloned package-swift from Github

Now, clone all of the required repositories:

# cd package-swift
# ./get.sh

On my SD card this step took about 15 minutes to download and write everything.

Next, apply patches. It is important to do the step to pick up patches that the Swift on ARM team has developed and tested. From time to time these patches change so make sure and pick up the latest.

# ./patch.sh

Now, compile. The Swift on ARM team uses Jenkins to run this in a headless build job, but if you use nohup to ensure your compile continues even if the terminal detaches you should be fine:

# nohup ./package.sh > swiftbuild.log&

To watch the output of the build use tail -F swiftbuild.log.

Now, be prepared to wait! A clean build of Swift on a Raspberry Pi 3 can take upwards of 6 hours. The reward is well worth it, however, and that is a swift.tar.gz bundle that can be installed on either a Raspberry Pi 2 or Pi 3 running Ubuntu Xenial (do not try to install this on a Raspbian machine, you will be sorry). I like to install Swift in /opt/swift/ like so:

# cd /opt
# mkdir -p swift/swift-3.0
# cd swift/swift-3.0
# tar -xzvf /path/to/swift.tar.gz

You can then set your PATH with export PATH=/opt/swift/swift-3.0/usr/bin:$PATH. To properly use swiftc and swift build you will also need to run the following commands:

sudo apt-get install -y libicu-dev
sudo apt-get install -y clang-3.6
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100

Getting Help

There is a high likelihood that your build will fail the first time. In fact, it is possible that builds for ARM devices such as the Raspberry Pi 2 and 3 will be failing for days until someone is available to fix them. If you are having trouble compiling please feel free to send an e-mail to admin@iachieved.it to get invited to the Swift for ARM Slack community.

Acknowledgements

I should probably put up a separate page just for this; a number of folks have been instrumental in getting Swift going on ARM devices. Folks like PJ have blazed the trail building Swift for the Raspberry Pi 1, and no article on Swift on the Pi is complete without mentioning William Dillon’s work.

4 comments

  1. Tim WoodReply

    When I hit this, the helpful folks in the swift-arm channel pointed out that this is a known bug in Xenial https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1579969 and gave a workaround: Look at the list of interfaces with ifconfig -a. One of these (for me at least) was named with a long string starting with ‘en’. Edit /etc/network/interfaces.d/50-cloud-init.cfg and replace the two occurrences of eth0 with the name of the ethernet device, reboot, and ethernet connections should be possible again.

Leave a Reply

Your email address will not be published. Required fields are marked *