There are a number of people working to bring Swift 3.0 to as many ARM-based systems as possible. I’m personally interested in the BeagleBoard family of devices and try Swift builds on a BeagleBone Black as soon as they are available. If you’re interested in joining the community of folks working on getting Swift 3.0 for ARM devices come join our community.
We’re assuming here you have a BeagleBone Black running Debian Jessie and have at least 1G of free space available. The base .tgz
distribution is nearly 200M, and around 600M extracted. If you’ve booted from a microSD card and taking advantage of its capacity we can show you how to get the most out of it.
The team working on Swift for ARM is using Jenkins to build natively on a BeagleBoard X15. You can find the latest build for BeagleBone Black here. Grab and extract the contents into a directory and set your PATH
with something like:
1 2 3 4 5 |
cd $HOME wget http://swift-arm.ddns.net/job/Swift-3.0-BBB-ARM-Incremental/lastBuild/artifact/swift-3.0-2016-09-19-BBB-ubuntu14.04.tar.gz mkdir swift-3.0 cd swift-3.0 && tar -xzf ../swift-3.0.tgz export PATH=$HOME/swift-3.0/usr/bin:$PATH |
Now, let’s give it a spin!
Create a file called helloWorld.swift
:
1 |
print("Hello, world!") |
You can use swift helloWorld.swift
to execute the file like a script:
# swift helloWorld.swift Hello, world!
Or, you can compile the file into an executable with swiftc
if you have clang
installed and configured properly:
# swiftc helloWorld.swift # ./helloWorld Hello world!
If swiftc
failed with error: link command failed with exit code 127
there’s a good chance you don’t have clang
installed and configured properly:
sudo apt-get update sudo apt-get install libicu-dev sudo apt-get install 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
Let’s look at a few other tidbits:
Importing Glibc
works!
swiftcat.swift
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import Glibc guard CommandLine.arguments.count == 2 else { print("Usage: swiftcat FILENAME") exit(-1) } let filename = CommandLine.arguments[1] let BUFSIZE = 1024 var pp = popen("cat " + filename, "r") var buf = [CChar](repeating:0, count:BUFSIZE) while fgets(&buf, Int32(BUFSIZE), pp) != nil { print(String(cString:buf), terminator:"") } exit(0) |
Compile (swiftc swiftcat.swift
) and run (swiftcat
)!
Bridging to C routines
Linking against compiled object files works!
escapetext.c
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <string.h> #include <stdlib.h> #include <curl/curl.h> int escapeText(const char* text, char** output) { int rc = -1; CURL* curl = curl_easy_init(); if (curl) { char* escaped = curl_easy_escape(curl, text, strlen(text)); if (escaped) { *output = (char*)malloc(strlen(escaped) + 1); strcpy(*output, escaped); curl_free(escaped); rc = strlen(*output); } } return rc; } |
escapetext.h
:
1 |
int escapeText(const char* text, char** output); |
escapeswift.swift
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import Glibc guard CommandLine.arguments.count == 2 else { print("Usage: escapeswift STRING") exit(-1) } let string = CommandLine.arguments[1] var buffer:UnsafeMutablePointer<Int8>? = nil let rc = escapeText(string, &buffer) guard rc > 0 else { print("Error escaping text") exit(-1) } if let escaped = buffer { let escapedString = String(cString:escaped) print("Escaped text: " + escapedString) } exit(0) |
Compile and link everything together:
# clang -c escapetext.c # swiftc -c escapeswift.swift -import-objc-header escapetext.h # swiftc escapeswift.o escapetext.o -o escapeswift -lcurl
And run:
# ./escapeswift "foo > bar" Escaped text: foo%20%3E%20bar
Swift Package Manager
Unless you enjoy writing makefiles and build scripts (and trust me, some do), the Swift Package Manager is here to help manage software package dependencies. We’ll be writing more about the SwiftPM available in Swift 3.0, but are excited to provide a version that works on armv7 devices. Try this out:
# mkdir finalCountdown && cd finalCountdown # swift package init --type executable Creating executable package: finalCountdown Creating Package.swift Creating .gitignore Creating Sources/ Creating Sources/main.swift Creating Tests/
Replace the contents of Sources/main.swift
with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import Foundation import Glibc let thread = Thread(){ print("Entering thread") for i in (1...10).reversed() { print("\(i)...", terminator:"") fflush(stdout) sleep(1) } print("\nExiting thread") print("Done") exit(0) } thread.start() select(0, nil, nil, nil, nil) |
Now, run swift build
and your finalCountdown
application:
# swift build Compile Swift Module 'finalCountdown' (1 sources) Linking .build/debug/finalCountdown # .build/debug/finalCountdown Entering thread 10...9...8...7...6...5...4...3...2...1... Exiting thread Done
moreswift
For a random smattering of Swift 3.0 applications that have been run on both x86 and armv7 systems, check out the moreswift swift-3.0 branch.
Build #66 works well, but when I use the code in the finalCountdown Package Manager example, I got an error on line 3 of main.swift about Foundation because it uses NSThread, and I guess Swift-3.0 Foundation just calls it Thread.
After fixing this, it works fine.
Thanks again for all the hard work.