iAchieved.it

Software Development Tips and Tricks

By

Working with MySQL Databases using Swift 3.0

If you’ve read our other Swift articles you’ll know that we’re big proponents of Swift on the server. Today we’ll keep with that theme by looking at working with MySQL databases with Vapor’s MySQL wrapper.

Warning: This is not a tutorial on MySQL or SQL. If you aren’t familiar with either there are plenty of tutorials out there. We’re going to focus specifically on working with MySQL with Swift 3.0 on Linux.

Getting Started

We chose MySQL 5.7 on an Ubuntu 16.04 system for this tutorial. MySQL 5.7 introduces a number of new features, one of which is the ability to store JSON data in a more efficient manner, as well as provide capabilities to select within the JSON data. More on this later. Since MySQL 5.7 is the default MySQL on Ubuntu 16.04 we’ll go with it as our OS.

If you don’t have Swift installed you can use our apt-get repo. See this post for instructions on setting it up. As of late September 2016 Apple also began building snapshots for Ubuntu 16.04. See Swift.org for more details.

Set up our database

Our database will be called swift_test, and it should be manipulated with a MySQL user swift whose password is swiftpass. If you’ve worked with MySQL for any period of time you are probably already chanting GRANT ALL ON swift_test.* and so on. So let’s set that up:

# sudo mysql
...
mysql> create user swift;
Query OK, 0 rows affected (0.00 sec)

mysql> create database swift_test;
Query OK, 1 row affected (0.00 sec)

mysql> grant all on swift_test.* to 'swift'@'localhost' identified by 'swiftpass';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> quit
Bye

Create a Swift Package

On to the code! Let’s create a package.

# mkdir swift_mysql
# swift package init --type executable

Update your Package.swift to look like:

Second, we’re going to be using a few helper routines to fill in some random data in our database. Create a file in the Sources directory called utils.swift and add the following:

Vapor MySQL

On to the real code, our main.swift file which will use the Vapor MySQL module.

Connecting to the Database

Add the following to Sources/main.swift:

Here we are setting up our database handle mysql. The initializer Database(host:String, user:String, password:String, database:String) is straightforward. The statement try mysql.execute("SELECT @@version") is a test to ensure we connected properly and can communicate with the database.

If our do block succeeds with no errors we can proceed to interacting with our database!

Ints and Strings

All of the calls made to MySQL will be through the execute(_:String) method. Note that this is different from using an abstraction API that provides methods like .create(table:String, ...) or .insert(table:String, .... execute takes raw SQL and passes it down through the MySQL connector.

Querying for results also uses the execute(_:String) method, but now we treat the result of the call as an array of [String:Node]. The keys of the dictionary are the column names returned.

The Node type here is a Vapor data structure that is used for converting amongst different representations. You can read more about it here. When using it with MySQL we take advantage of the Node properties int, string, object to convert from its agnostic representation to a Swift type. Thus, let bar = result["bar"]?.int gives us an Int.

Moving On

Now let’s look at a more advanced example with creating a table that contains MySQL DATE, POINT, and JSON datatypes.

Our table is called samples.

To insert a date into the database using a SQL statement we will need to appropriately format it:

Now let’s create a POINT as a Swift tuple:

Finally, we want to utilize MySQL 5.7’s new JSON datatype, and moreover we’ll use the Jay package in Swift to quickly create a JSON-string from a Swift [String:Any] dictionary.

Hint: You do not have to explicitly call out Jay as a dependency in the Package.swift file because it is included transitively by the MySQL package.

We now want to convert this to a String suitable for giving to MySQL:

Now we have our date, point, and JSON string (sample), let’s insert the data into the samples table:

Note we did use a bit of a trick with the POINT in that \(point) will expand in our string to (37.20262, -112.98785), thus the full string will be POINT(37.20262, -112.98785) which is what MySQL will expect. The entire statement string looks like:

Retrieving Results

Warning! As of this writing (September 22, 2016), there is a bug in Vapor MySQL 1.0.0 that crashes on reading a POINT datatype, so we’ll have to make do and not use SELECT * below. We’ve filed this issue against Vapor MySQL and will update the post once the issue is fixed.

In the example below we’re going to take advantage of MySQL 5.7’s ability to include JSON data in the SELECT ... WHERE clauses. Here we are looking for those samples where the speed field of the JSON data in the sample is greater than 80.

A couple of notes here. The JSON_EXTRACT function is used to return data from a JSON document, selected from the parts of the document matched by the path arguments. In our case here we want to extract the value at the path $.speed from the sample column. To extract the longitude value from our JSON document we would use the path $.gps.longitude.

To iterate over our results we use the for result in results construct in Swift, and then the if let construct to validate our result data. First, use let sample = result["sample"]?.object to get a dictionary that is built from the MySQL JSON document. This is key here! The Vapor MySQL library does not return back a String object that needs to be handed to a JSON parser; that work is already done for you, so you can begin accessing the sample dictionary directly.

The remaining lets give us our speed, temperature, and created_at. Note that created_at is a MySQL DATETIME which will read in as a String. To convert to a Date in Swift you will need to use a DateFormatter with .date(from:String).

Get the Code!

If you want to get started with less typing, check out the code from Github. Build everything with swift build, but before running the executable remember that you have to have the database, user, and grants already configured.

By

Handling Dates with Swift 3.0

Swift 3.0

When you take on a goal as ambitious as the Great Renaming it is a challenge to ensure that all of the reference documentation is updated. As of September 20, 2016, for example, the DateFormatter documentation contains inconsistencies and references to Swift 2.3-style APIs. As time passes this will undoubtedly be corrected, but in the meantime here are a few examples for formatting dates with the Date and DateFormatter.

The current example in the above-reference documentation is:

In Swift 3.0 this changes to:

Notice that .mediumStyle is dropped in favor of .medium. This is in line with simplified naming; we know that the type is of DateFormatter.Style so there is no reason to repeat the word Style. The same applies for .none as opposed to .noStyle.

Now, let’s look at the changes to setting the formatter’s locale:

Again, we see the simplification from Locale(localeIdentifier:) to Locale(identifier:). Less typing and to the point. Likewise the stringFromDate method of the DateFormatter has been streamlined to string(from:), which if you use the entire signature is string(from:Date). See the pattern?

Moving on to creating a Date from a String representation, the Apple documentation has this example:

Applying the principle of reducing verbiage and unnecessary words we arrive at:

The TimeZone initializer drops an extraneous three characters (for), and as expected the dateFromString method becomes date(from:).

Rules of Thumb

It should be apparent that the general rule of thumb when transitioning from Swift 2 to Swift 3 is to remove superfluous words. If you were used to writing formatter.timeStyle = .fullStyle before, get used to writing formatter.timeStyle = .full. If you see someTypeFromAnotherType() it’s likely been replaced with someType(from:AnotherType).

Speaking from experience, after working with Swift 3 for several months now, going back to Swift 2 feels overly verbose, and this is coming from someone who actually likes verbose languages. Once you get the hang of it you’ll be embracing Hemingway and eschewing Tolstoy.

Happy Swifting!

By

Faster Carthage Builds

I love Carthage for managing external dependencies in my Xcode projects. What I don’t care for is that most READMEs have you edit your Cartfile and then run carthage update. By default this will compile the dependencies for every target that the 3rd party framework supports: iOS, tvOS, watchOS, and macOS. If I’m working on an iOS-only project that’s a significant waste of my time.

Lucky for us you can bypass all that and specify only the platform you want to build for with the --platform argument:

notification git:(master) carthage update --platform ios
*** Fetching SQLite.swift
*** Checking out SQLite.swift at "8b95a0c4a884f35e5102cd306f634680255473bb"
*** xcodebuild output can be found in /var/folders/b_/mwxmgbj530l2l5jzl6ygnqtr0000gn/T/carthage-xcodebuild.P0EL0n.log
*** Building scheme "SQLite iOS" in SQLite.xcodeproj

You can also specify a number of platforms using a comma-separated list: carthage update --platform tvos,ios.

The --platform argument can also be used with the build command: carthage build --platform ios

So, stop wasting time building for platforms you don’t need. Use --platform and call out only those your project supports!

By

Notifications and userInfo with Swift 3.0

Xcode 8.0 Swift 3.0

Xcode 9.3 Swift 4.1

Editor’s Note: This is one of our most popular posts, so I wanted to take a moment and verify that the Swift 3.0 code presented below is still accurate with Swift 4.1. I’m happy to say it still is, so enjoy posting Notifications with userInfo in Swift 4!

Swift 3.0 has brought a number of changes to the Swift language, including the Great Renaming which brought about the end of the NS prefix on Foundation classes. NSThread is now simply Thread. NSData becomes Data. You get the idea.

That means we need to provide an update on using NSNotificationCenter, sorry, NotificationCenter with userInfo. Things have definitely changed between Swift 2 and Swift 3.

The technique for obtaining the default NotificationCenter has changed, and can now be done with let nc = NotificationCenter.default. In addition the model of using selectors has changed to specifying a block or funtion to execute when the notificaiton is received.

For example, in Swift 2 we would write:

whereas Swift 3 code would look like this:

In this example we’re instructing the notification center to deliver MyNotification notifications to the catchNotification function which has a signature of (Notification) -> Void. Alternatively we could use a trailing closure:

Post It!

Now, let’s look at posting (sending) a notification. The postNotificationName method in Swift 2.0 has been replaced with post in Swift 3.0.

The userInfo now takes [AnyHashable:Any]? as an argument, which we provide as a dictionary literal in Swift. Note that the userInfo values don’t need to be homogeneous (that’s where the Any comes in); we are sending along a String and a Date.

Handling Notifications

The guard construct serves as a good method to unwrap and verify that the expected data is in the userInfo.

To verify that the guard works properly switch out the Date() in the call to post with a String or some other object. You should see No userInfo found in notification printed to the console.

Example Source

You can try out the code above with a simple iOS project. Create a new Single View Application and replace the contents of ViewController.swift with the following:

A few notes here:

  • Notification “names” are no longer strings, but are of type Notification.Name, hence why we declare let myNotification = Notification.Name(rawValue:"MyNotification"). This allows us to use myNotification anywhere a Notification.Name is expected, i.e., the NotificationCenter.addObserver and NotificationCenter.post functions.
  • We chose to have a separate func for catchNotification here rather than utilizing a trailing closure.

And that’s it! Simple and effective.

By

Xenial Xerus Ethernet Fixup for the Raspberry Pi

Xenial Xerus has a peculiar bug that can cause a Raspberry Pi to lose Ethernet connectivity after a reboot. Several individuals noticed it after an apt-get update && apt-get -y upgrade while following instructions for building Swift on a Pi 3.

To fix this, power down the Pi, remove the microSD card and reinsert it into another system and then mount the Pi filesystem with something like:

# mkdir pifs
# mount /dev/sdX2 pifs

where X depends on where your microSD was mapped. I turn to dmesg|tail to find this:

[6099875.497524] usb-storage 2-4:1.0: USB Mass Storage device detected
[6099875.497771] scsi host21: usb-storage 2-4:1.0
[6099876.498806] scsi 21:0:0:0: Direct-Access     Generic  STORAGE DEVICE   0817 PQ: 0 ANSI: 6
[6099876.499682] sd 21:0:0:0: Attached scsi generic sg8 type 0
[6099876.832189] sd 21:0:0:0: [sdi] 31293440 512-byte logical blocks: (16.0 GB/14.9 GiB)
[6099876.833257] sd 21:0:0:0: [sdi] Write Protect is off
[6099876.833266] sd 21:0:0:0: [sdi] Mode Sense: 23 00 00 00
[6099876.834375] sd 21:0:0:0: [sdi] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[6099876.840573]  sdi: sdi1 sdi2
[6099876.845072] sd 21:0:0:0: [sdi] Attached SCSI removable disk

In this case /dev/sdi2 is used.

Once your filesystem is mounted navigate over to the Pi’s /var/log/ directory:

# mount /dev/sdX2 pifs
# cd pifs/var/log

Using grep -a, search for the phrase renamed from in syslog:

root@darthvader:/tmp/pifs/var/log# grep -a "renamed from" syslog
Sep  4 21:59:59 ubuntu kernel: [    5.533471] smsc95xx 1-1.1:1.0 enxb827eb9721d5: renamed from eth0

Notice the new Ethernet device name enxb827eb9721d5. Now go to the /etc/network/interfaces.d/ directory on the Pi filesystem:

root@darthvader:/tmp/pifs/var/log# cd ../../
root@darthvader:/tmp/pifs# cd etc/network/interfaces.d/

Edit 50-cloud-init.cfg and replace instances of eth0 with the new device name enxb827eb9721d5:

# cat 50-cloud-init.cfg
auto lo
iface lo inet loopback

auto enxb827eb9721d5
iface enxb827eb9721d5 inet dhcp

Unmount the filesystem:

# cd /tmp/
# umount pifs

Return the microSD card to the Pi and reapply power. Your Pi’s Ethernet should now be available.

A special thanks to @tjw for providing clues to the fix for this issue!

By

error: invalid inferred toolchain

Invalid inferred toolchain? What?!

You’ve installed swiftenv on your shiny new Ubuntu system, obtained one of the Apple releases with swiftenv install, but when you run swift build you get error: invalid inferred toolchain. That’s no good.

Fortunately the fix is simple, you need to install clang:

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

In this example we’re using clang-3.6 but any version 3.6 or higher should be fine.

By

MQTT Last Will and Testament

This is another post in a series on writing MQTT clients using Swift on Linux.

In this post we’ll look at the MQTT Last Will and Testament message. Essentially the LWT message is predefined by a client connecting to the broker. In the event the client abnormally disconnects, the broker will then broadcast the LWT to any subscribers of the LWT message topic.

For example, let’s say you are building a chat application where each client connects to a broker and then subscribes to the topic /chat/hottub. Messages then published to /chat/hottub are received by all of the subscribers. A simple but effective way to join a “chat room” (i.e., MQTT topic).

When a client leaves the chat under normal conditions we might expect a message like “Joe has left /chat/hottub.” This would be easy to do; when the user types /exit then publish an appropriate message and then terminate the client. If a client abnormally disconnects (loss of network, client crash, etc.), then what was given to the broker as the Last Will and Testament message is used instead.

Here’s how we set up the Last Will message for our MQTT client:

willMessage is a MQTTWill property of the MQTT class. MQTTWill is constructed with a topic and message. In our example here the topic will be our chat channel /chat/hottub, and the message will be a JSON string that contains our client’s ID and a simple Abnormal Disconnect string.

Get the Code

We’ve been building Swift 3.0 directly from the latest in the Apple repositories and continually upgrade our code. To use these examples you’ll need to install Swift 3.0 from our apt-get repository.

An example of our MQTT HotTub is in GitHub.

# git clone https://github.com/iachievedit/MQTTHotTub
# cd MQTTHotTub
# swift build

To test things out you’ll want to run the MQTTHotTub twice, so open a second terminal.

1__clear_____build_debug_MQTTHotTub__ssh__and_9__clear_____build_debug_MQTTHotTub__ssh_

Run the clients with .build/debug/MQTTHotTub.

Prisencolinensinainciusol

Our MQTTHotTub client simulates a chat of gibberish. Each published message is delivered as JSON:

{"client":"octxktfo", "message":"Gyxswhz nsoxfnj gz."}
{"client":"ajyhyjic", "message":"Cmr w bzwubzv mwfhtklz."}

When a client receives a message that it did not send it gets filtered out:

Remember, if we are listening to messages published to a given topic, and we publish to that topic, the client will also receive that message back as an echo. This is the purpose of filtering with cid != clientId.

You can see each client logging what they are receiving from other clients:

Received "Wlfu zrqyj tady obxnjl lupihobi nph oapplt nyidmja." from octxktfo
Received "Cmr w bzwubzv mwfhtklz." from ajyhyjic

Now, CTRL-C one of the clients, and notice what is received by the remaining client(s):

Received "Abnormal Disconnect" from octxktfo
Abnormal Disconnect

Abnormal Disconnect

This is the MQTT Last Will and Testament message in action; the aborted client had no opportunity to broadcast that it was unavailable so instead the broker sent out the client’s Last Will on the /chat/hottub topic:

{"client":"\(clientId)","message":"Abnormal Disconnect"}

Do I Need to Have a Last Will?

Need is a strong word, and the answer is no, your MQTT client does not need to supply a Last Will message. There are plenty of additional examples that provide guidance on when you might want to use one.

What’s Next?

We’re continuing to work hard on our MQTT implementation for Swift on Linux. With Last Will and Testament now working our attention will turn to implementing secure MQTT connections (MQTT SSL). Stay tuned!

By

Compiling Open Source Swift Foundation

I recently came across an issue with the NSThread implementation in open source Swift Foundation. I wouldn’t have ever found the issue if it hadn’t been for trying to run this code on a Raspberry Pi 3:

Our expectation here is that we will be creating and destroying a thread every second. Unfortunately, after approximately 230 threads the system resources had been exhausted and no more threads would actually start. In the end, as you can see from SR-1908, the solution was to initialize threads with system scope and a state of detached:

Philippe Hausler proposed the solution in SR-1908, and since I had a Raspberry Pi 3 to test it out on I took on the task of implementing and testing it.

Building Only Foundation

If you read the Getting Started documentation for Open Source Foundation it’s pretty clear that you need to first build Swift, clang, and llvm. If I were working on a tricked out server with plenty of CPU and fast disk, I wouldn’t mind. On a Raspberry Pi 3 though, as much of an improvement it is over its older siblings, it is still a little slow. I could also look into cross-compiling Swift, but haven’t yet rustled up the time to figure that out (and if you’ve ever worked with cross-compile environments you know it takes a good amount of time to get set up).

What we need is the ability to take advantage of an existing build tree and compile Foundation by itself. It turns out you can, otherwise, we wouldn’t have a blog post on this now would we?

Here’s what you’re going to need to do this (regardless of whether you run this on an x86 server or an ARM computer like a BeagleBone or Raspberry Pi):

  • a fully built swiftc, typically found in build/buildbot_linux/swift-linux-armv7/bin
  • a fully built swift, also in the build/buildbot_linux/swift-linux-armv7/bin directory
  • a fully built clang (the one built from the open source repositories), found in build/buildbot_linux/llvm-linux-armv7/bin directory

I’m looking to provide a “toolchain” of sorts with all of these already compiled, but for now you’ll have to build your own first. Then you will be able to build Foundation on its own.

Now, let’s look at how you use this to test something out on Foundation. Notice that we are cloning our own fork of swift-corelibs-foundation. This is important if you’re planning on issuing pull requests back into the upstream repository (i.e., Apple’s repository).

# git clone https://github.com/iachievedit/swift-corelibs-foundation
# export PREBUILT_ROOT=/root/workspace/Swift-3.0-Pi3-ARM-Incremental/build/buildbot_linux/
# SWIFTC=$PREBUILT_ROOT/swift-linux-armv7/bin/swiftc \
CLANG=$PREBUILT_ROOT/llvm-linux-armv7/bin/clang      \
SWIFT=$PREBUILT_ROOT/swift-linux-armv7/bin/swift     \
SDKROOT=$PREBUILT_ROOT/swift-linux-armv7             \
BUILD_DIR=build ./configure Debug
# /usr/bin/ninja
...
[290/290] Link: build/Foundation/libFoundation.so

First, we set an environment variable PREBUILT_ROOT to where our prebuilt Swift and associated tools are. This is used in the next step which, if you strip away the environment variables is ./configure Debug (you can use Release here as well). But, we need the environment variables SWIFTC, CLANG, SWIFT, and SDKROOT to point the configure script to our “toolchain.” Finally, the BUILD_DIR environment variable sets where all of the intermediate artifacts and final output (libFoundation.so) will be placed.

Note: I shouldn’t have to say this but somtimes you’d be surprised by what gets left in the comments. Your PREBUILT_ROOT is the location of your preexisting toolchain. Don’t expect to find anything on your system at /root/workspace/Swift-3.0-Pi3-ARM-Incremental!

Finally, /usr/bin/ninja runs our build. Once complete you should have a libFoundation.so shared library in your build/Foundation/ directory. To test it with an existing installation of Swift just copy libFoundation.so over to $YOUR_SWIFT_ROOT/usr/lib/swift/linux/libFoundation.so.

Running testcases

You can also run the Foundation test suite by adding a -DXCTEST_BUILD_DIR parameter to ./configure

# export PREBUILT_ROOT=/root/workspace/Swift-3.0-Pi3-ARM-Incremental/build/buildbot_linux/
# SWIFTC=$PREBUILT_ROOT/swift-linux-armv7/bin/swiftc \
CLANG=$PREBUILT_ROOT/llvm-linux-armv7/bin/clang      \
SWIFT=$PREBUILT_ROOT/swift-linux-armv7/bin/swift     \
SDKROOT=$PREBUILT_ROOT/swift-linux-armv7             \
BUILD_DIR=build ./configure Debug                    \
-DXCTEST_BUILD_DIR=$PREBUILT_ROOT/xctest-linux-armv7
# /usr/bin/ninja test
[4/4] Building Tests
**** RUNNING TESTS ****
execute:
LD_LIBRARY_PATH= build/TestFoundation/TestFoundation
**** DEBUGGING TESTS ****
execute:
LD_LIBRARY_PATH= lldb build/TestFoundation/TestFoundation

Running the tests requires LD_LIBRARY_PATH to be supplied with two paths: one to the libXCTest.so shared library, as well as the path to the “library under test”. We accomplish this as follows, assuming that our libFoundation.so is in ./build/Foundation, which would it would be if we were following the steps above.

# LD_LIBRARY_PATH=./build/Foundation:$PREBUILT_ROOT/xctest-linux-armv7 ./build/TestFoundation/TestFoundation
...
Test Suite 'All tests' passed at 03:16:45.315
     Executed 483 tests, with 0 failures (0 unexpected) in 37.621 (37.621) seconds

Closing Thoughts

I need to stress here that to use this technique you do need a preexisting “build toolchain” that has a Swift, clang, and llvm. Moreover, the further time passes from when your toolchain was last built to when you try to build Foundation on its own, the higher the risk that your Foundation relies on language features that weren’t present when you built your toolchain. However, if you decide to embark on working with Foundation, build your full Swift toolchain first and save the build directory to employ the technique described above.

Good luck!

By

Upgrading CMake for a Happier Swift Build

There have been a number of updates to Open Source Swift that take advantage of newer versions of CMake to build swift on Linux. In particular, the default version of cmake that comes with Ubuntu 14.04 (2.8.12.2) is no longer up to the task.

Let’s get our Ubuntu 14.04 environment updated with CMake 3.4.3, the version proposed by the developers that work in this area.

On systems I’m installing software by source, I usually have a build area in /usr/local/src and an area of archives (so I can keep track of the versions I built) in /usr/local/archive:

As root or using sudo:

# cd /usr/local/archive
# wget https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz
# cd ../src/
# tar -xzvf ../archive/cmake-3.4.3.tar.gz

Now, to configure and build:

# cd cmake-3.4.3
# ./configure --prefix=/usr/local
...
CMake has bootstrapped.  Now run make.
# make

Finally, make install will install cmake and its associated configuration to /usr/local.

# make install
# which cmake
# cmake --version
cmake version 3.4.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

That’s all it takes. Happy building!

By

An Example of Scripting with Swift on Linux

Swift 3.0 Linux

If you follow us on Twitter (@iachievedit) you know that we build open source Swift a lot, and for a number of different OSes and architectures. There’s no rhyme or reason as to when we decide to start building, but it’s nice to keep track of the various git revisions of the repositories that went into a given artifact.

Since we’re excited about Swift eventually displacing other scripting languages on Linux (their names rhyme with Bython and Buby), it felt like a good time to write up a script (in Swift) that did this for us.

Some notes here:

  • we’re using a subscript extension of String that will likely never be a part of the core language for reasons.
  • exec is our version of backticks (output = `ls`) in other scripting languages. We want to execute a command and read its output.
  • there’s certainly more optimization and tightening that can be done.

With that out of the way, here is swiftrevs.swift:

We use this as follows:

# swift swiftrevs.swift
swift,https://github.com/apple/swift.git,master,cf3fdff04
llvm,https://github.com/apple/swift-llvm.git,stable,8d0086ac3
clang,https://github.com/apple/swift-clang.git,stable,460d629e8
lldb,https://github.com/apple/swift-lldb.git,master,da120cf91
compiler-rt,https://github.com/apple/swift-compiler-rt.git,stable,9daa1b32c
cmark,https://github.com/apple/swift-cmark.git,master,5af77f3c1
llbuild,https://github.com/apple/swift-llbuild.git,master,7aadcf936
swiftpm,https://github.com/apple/swift-package-manager.git,master,423c2a1c8
swift-corelibs-xctest,https://github.com/apple/swift-corelibs-xctest.git,master,03905f564
swift-corelibs-foundation,https://github.com/apple/swift-corelibs-foundation.git,master,4c15543f8
swift-integration-tests,https://github.com/apple/swift-integration-tests.git,master,4eda8055a
swift-corelibs-libdispatch,https://github.com/apple/swift-corelibs-libdispatch.git,master,e922531c2

This, of course, is run in a directory that has all of the various repositories required to build Swift cloned. If you’re interested in learning how to build Swift for X86 or certain ARM devices, check out our package-swift repository. It not only contains swiftrevs.swift but also a handful of scripts aimed at making building Swift (along with the REPL, Foundation, and Swift Package Manager) nearly painless.