Skip to content

Instantly share code, notes, and snippets.

@NobodyNada
Last active June 8, 2017 19:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NobodyNada/b19d227b121b4277305f925c4c3419e9 to your computer and use it in GitHub Desktop.
Save NobodyNada/b19d227b121b4277305f925c4c3419e9 to your computer and use it in GitHub Desktop.
Compiling Swift on Raspberry Pi

Compiling Swift on Raspberry Pi

This guide was written for FireAlarm, but it will work for other Swift projects as well.

Note: Many of these steps will take a long time (up to several hours). To run a command in the background (so it won't die with your SSH session), I often run the command with time (<command here>) > filename.log 2>&1 &.

Update and install dependencies

Because some packages are not available in the latest stable release, Raspbian must be updated to Stretch, the testing release. Unfortunately, the Wi-Fi firmware in Stretch is broken. To prevent it from being updated, run sudo apt-mark hold firmware-brcm80211. If you update to Stretch without doing this (which I did), you can downgrade by running sudo apt-get install firmware-brcm80211=0.43+rpi5 (while connected over Ethernet, of course).

Edit /etc/apt/sources.list and replace jessie with stretch in the first line. The first line should now look like this:

deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi

Now run sudo apt-get update && sudo apt-get -y dist-upgrade -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" to upgrade all installed packages, including the kernel and firmware. On my system, this took several hours. The dpkg options force the default behavior when replacing configuration files, so it doesn't pause for a prompt.

Install Swift dependencies with:

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

Build Swift

I used these shell scripts to make it easier to download and build Swift:

download.sh:

#!/bin/bash

FORCE=0

while getopts "f" option; do
    case $option in
        f)
            FORCE=1
            ;;
        ?)
            echo "Usage: download.sh [-f]"
            ;;
    esac
done

mkdir -p build
cd build


REPOS=('swift'       'swift-llvm' 
       'swift-clang' 'swift-lldb'           
       'swift-cmark' 'swift-corelibs-xctest' 
       'swift-corelibs-foundation' 'swift-corelibs-libdispatch')

DIRS=( 'swift'       'llvm'       
       'clang'       'lldb'                  
       'cmark'       'swift-corelibs-xctest'
       'swift-corelibs-foundation' 'swift-corelibs-libdispatch')


for ((i=0;i< ${#REPOS[@]};i++)) do
    REPO=${REPOS[$i]}
    DIR=${DIRS[$i]}
    BRANCH=${BRANCHES[$i]}
    if [ ! -d "$DIR" ] || [ $FORCE = 1 ]; then
        [ -d "$DIR" ] && rm -rf "$DIR"
        git clone "git://github.com/apple/${REPO}.git" "$DIR" -b swift-3.1-branch
    else
        echo "$REPO already exists; not overwriting without -f"
    fi
done

pushd swift-corelibs-libdispatch
git submodule init && git submodule update
popd

build.sh:

#!/bin/bash

BUILD_DIR=$(pwd)/build

cd "$BUILD_DIR" || (echo "Directory '$BUILD_DIR' doesn't exist."; exit 1)
DATE=$(date +%F)

swift/utils/build-script --no-assertions --no-swift-stdlib-assertions \
--release --libdispatch --lldb --foundation --install-xctest \
--install-foundation --install-libdispatch --install-lldb \
--swift-enable-ast-verifier=0 --install-swift --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="$BUILD_DIR/dest" \
--installable-package="$BUILD_DIR/swift-$DATE.tar.gz" --release

After running download.sh, we need to make a few patches for it to build.


The swift_build_support import path in SwiftBuildSupport.py needs to have priority. Change this line (line 23 when I built):

sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))

to:

sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'swift_build_support'))

Go to the declaration of COMMON_C_FLAGS in swift/utils/build-script-impl. On my system, this was line 1525. It should contain:

COMMON_C_FLAGS=""

Edit it to:

COMMON_C_FLAGS="-mfpu=vfpv4"

This disables NEON instructions, which were having an alignment issue. I'll try to come up with a real fix, but this works for now.


If you want LLDB to work, change the definition of LLDB_EDITLINE_USE_WCHAR on line 50 of lldb/include/lldb/Host/Editline.h to 1.


LLVM's swift-3.1-branch Unicode functions are not in the llvm namespace, where Swift expects to find them. cd to llvm and run git cherry-pick 449cdb5ad51c7720c9f5eecdcb1d142044f16721 to cherry pick a commit which fixes this.

Unfortunately, Clang's swift-3.1-branch does not expect to find the files in the LLVM namespace. Cherry-pick a commit to fix this by cding to clang and running git cherry-pick dd052623fdb8f7292a60486e46c498a4294ad581.


There are some bugfixes in Foundation's swift-3.1-branch which are not in master, and vice versa. To fix it, cd to swift-corelibs-foundation and run git merge --strategy-option theirs master.


CoreFoundation makes an assumption about structure size which is not true on Raspberry Pi (because of alignment). Go to swift-corelibs-foundation/CoreFoundation/NumberDate.subproj/CFNumber.c and change this line (line 1203 when I built):

CFIndex size = 8 + ((!__CFNumberTypeTable[type].floatBit && __CFNumberTypeTable[type].storageBit) ? 8 : 0);

to:

CFIndex size = sizeof(struct __CFNumber) - sizeof(CFRuntimeBase);
if (!__CFNumberTypeTable[type].floatBit && __CFNumberTypeTable[type].storageBit) size += 8;

In order for the Swift JIT interpreter to work (which is necessary for the Swift Package Manager), cd to llvm and cherry-pick a commit from https://github.com/swift-arm/swift-llvm:

git remote add swift-arm https://github.com/swift-arm/swift-llvm
git fetch swift-arm
git cherry-pick 95581a28b69cc7ea811055891b499576fdfc8ed7

You may get a few more compiler errors. Here's how to fix the ones I ran into:

use of undeclared identifier 'llvm_unreachable'

Add an #include "llvm/Support/ErrorHandling.h" to the file.

'linux/membarrier.h file not found

This is because the version of linux-libc-dev in Raspbian is ancient and is missing membarrier.h. To work around it, replace the #include on line 93 of swift-corelibs-libdispatch/src/shims/lock.h with the code that is supposed to be in that (thankfully tiny) header and the syscall number definition for membarrier:

enum membarrier_cmd {
        MEMBARRIER_CMD_QUERY = 0,
        MEMBARRIER_CMD_SHARED = (1 << 0),
};
#define __NR_membarrier			(__NR_SYSCALL_BASE+389)

use of undeclared identifier 'DISPATCH_INTERNAL_CRASH'

This is because something's messed up with header files. Replace the line with the error with an abort(); (and maybe a printf for debugging). If you get a linker error mentioning DISPATCH_INTERNAL_CRASH, clean libdispatch (by removing build/build/Ninja-Relase/libdispatch-linux-armv7) and try again.


Once the build completes successfully, it's time to install Swift. The build script will generate a .tar.gz file contaiing the Swift binaries, which you can install in /usr/bin, /usr/lib, etc. by running sudo tar -C / -xzf filename.tar.gz. You can omit the -C / flag to install it in the current directory, which makes it far easier to uninstall Swift (but be sure to add the created usr/bin directory to your PATH).

For whatever reason, the installable package was missing swift-autolink-extract. This caused swiftc to fail silently, compiling the code successfully but not generating an executable. I fixed this by copying build/build/Ninja-Release/swift-linux-armv7/bin/swift-autolink-extract into /usr/bin.

Additionally, the Dispatch module is installed into the wrong directory. Copy Dispatch.swiftdoc and Dispatch.swiftmodule from /usr/lib/swift/linux/armv7l into /usr/lib/swift/linux/armv7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment