Skip to content

Swift on Raspberry Pi

Scott James Remnant edited this page May 12, 2018 · 8 revisions

Prev: Raspberry Pi Setup


Swift is my favorite programming language of the moment, and it's combination of a high-level syntax while still being compiled to native code, make it an attractive option for working with the Raspberry Pi hardware.

Unfortunately while the Linux port is an actively maintained and supported part of the project, it's intended for Intel processors.

The 32-bit ARM processor in the Raspberry Pi isn't natively supported by Swift, but there is an active community that attempts to make it work.

With a little effort, and with the patches from uraimo's buildSwiftOnARM repository, we can build a working toolchain.

Installing dependencies

Swift's build dependencies are documented in the README of the main repository.

sudo apt-get install 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 libblocksruntime-dev libcurl4-openssl-dev autoconf libtool systemtap-sdt-dev

As of stretch, these are almost all of the correct versions.

Updating swig

The one exception is that the swig package in stretch is, unluckily, one of a couple of minor revisions that are known not to work with the Swift build process.

To work around this, we use the source from Debian unstable to build a version that will work:

wget http://http.debian.net/debian/pool/main/s/swig/swig_3.0.12-1.dsc \
     http://http.debian.net/debian/pool/main/s/swig/swig_3.0.12.orig.tar.gz \
     http://http.debian.net/debian/pool/main/s/swig/swig_3.0.12-1.debian.tar.xz
dpkg-source -x swig_3.0.12-1.dsc
sudo apt-get install bison debhelper dh-autoreconf default-jdk guile-2.0-dev libchicken-dev libperl-dev libpcre3-dev python-dev ruby ruby-dev tcl-dev tk-dev
(cd swig-3.0.12 && dpkg-buildpackage -uc -us)
sudo dpkg -i swig_3.0.12-1_armhf.deb swig3.0_3.0.12-1_armhf.deb

Downloading the source

A source build of Swift consists of more than the main repository for the language, but also the compiler, debugger, standard library, support libraries, and more.

We'll create a working directory to serve as the top-level for the build:

mkdir swift-source
cd swift-source

The master or HEAD of the repositories is the latest development version, intended for release alongside the next version of Xcode. We rarely want something that bleeding edge, and thanks to the community nature of the 32-bit ARM support, it rarely works anyway.

To checkout the Swift 4.1.1 release, we use the swift-4.1.1-RELEASE tag:

git clone -b swift-4.1-RELEASE https://github.com/apple/swift.git
./swift/utils/update-checkout --clone --tag swift-4.1-RELEASE

Swift 4.1.1 needs a few patches in order to build, which we can obtain from the buildSwiftOnARM project. We'll checkout that alongside our sources as well:

git clone https://github.com/uraimo/buildSwiftOnARM.git

We'll then apply those to the appropriate repositories:

for PATCH in buildSwiftOnARM/*.diffs/*.diff; do \
  ( cd $(echo "$PATCH" \
    | sed -e "s,[^/]*/,,;s,.diffs/.*,,") \
  && patch -p1 <  ../"$PATCH"  ); \
done

Building

Swift comes with a script that manages building all of the different components and dependencies in the correct order (and in some cases, rebuilding them again).

The following arguments are those currently recommended for the Raspberry Pi:

./swift/utils/build-script \
        --build-subdir out_linux \
        -R \
        --lldb \
        --llbuild \
        --xctest \
        --swiftpm \
        --foundation \
        --libdispatch \
        -- \
        --install-libdispatch \
        --install-foundation \
        --install-swift \
        --install-lldb \
        --install-llbuild \
        --install-xctest \
        --install-swiftpm \
        --install-prefix=/usr \
        '--swift-install-components=autolink-driver;compiler;clang-builtin-headers;stdlib;swift-remote-mirror;sdk-overlay;dev' \
        --build-swift-static-stdlib \
        --build-swift-static-sdk-overlay \
        --install-destdir=$(pwd)/install \
        --installable-package=$(pwd)/swift-4.1.1.tar.gz \
        --verbose-build

This will create a build/ sub-directory containing the intermediate build output, a tree under install/ containing the staged for installation, and a swift-4.1.1.tar.gz package file of that tree.

Installing

Since Swift's support for the Raspberry Pi varies between releases, it's good practice to keep a working toolchain around as long as possible, and always give yourself the chance to rollback to a last known good one.

To achieve this, I like to install the binaries underneath /opt/swift with a tree for each version, rather than just unpacking it all into /usr each time.

Symlinks manage the current version:

sudo mkdir -p /opt/swift
sudo tar xzf swift-4.1.1.tar.gz -C /opt/swift
sudo mv /opt/swift/usr /opt/swift/4.1.1
sudo ln -sf /opt/swift/4.1.1/* /opt/swift

Add /opt/swift/bin to your PATH for convenience:

echo 'PATH=$PATH:/opt/swift/bin' >> ~/.profile

Testing

We can now test it out.

swift --version
Swift version 4.1.1 (swift-4.1.1-RELEASE)
Target: armv7-unknown-linux-gnueabihf
mkdir HelloWorld
cd HelloWorld
swift package init --type executable
Creating executable package: HelloWorld
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/HelloWorld/main.swift
Creating Tests/
swift build
Compile Swift Module 'HelloWorld' (1 sources)
Linking ./.build/debug/HelloWorld
swift run HelloWorld
Hello, world!

Next: Integers