Linux compatible Swift and Swift Package Manager

Apple opensourced Swift today, as promised in early 2015. This is great, awesome etc... but I'm not about that here. Together with Swift - programming language, Apple presented Linux port of Swift compiler and some tools. One of the tools is Swift Package Manager - that's the reason of this post.

The Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.

After back home today, excited about everything, I wanted try this new toy, NOW. I thought: why not build CryptoSwift for Linux (btw. CommonCrypto is not available outside iOS/OSX - is not part of the Foundation).

So I did.

First of all - I'm happy user of Parallels Virtual Machines. As always, this time I was not disappointed, Ubuntu virtual machine is literally 3 clicks away, just like this:

Ubuntu because Apple decided provide precompiled packages only for Ubuntu.

of course you can build it all from sources - I'm just too old for that 😂 My Linux time as PLD developer is the past. I'm comfortable with that. I still remember how to do Linux maintenance... it's just not my pair of shoes.

Back to the Swift.

Installation is like "just works". Enough unzip downloaded file and that's all. Seriously.

For Package Manager you'll need install Git command line tool, it's not bundled in Swift package.

sudo apt-get install git

Linux ready

I thought that CryptoSwift is already prepared for Linux. Some time ago I separated all Foundation dependency - turn out unnecessary because Apple decided open source Foundation framework too (it have some glitches still).

Today turns out it's not enough.

First... there is no Darwin module (you will notice lack of few modules, for example simd module)

error: no such module 'simd'

next, I replaced Darwin with Glibc for linux with this conditional code:

#if os(Linux)
    import Glibc
#else
    import Darwin
#endif

of course Glibc is not exactly equivalent of Darwin module. It is Glibc, not Darwin after all. What I found is that there is no arc4 random number generator functions arc4random(). This is because arc4random() is part of BSD systems by default.

I started looking into swift code and found out that swift internally use arc4random() with function _swift_stdlib_arc4random_uniform from private module SwiftShims.

Some say

It's not intended to be API

and I believe, but not really have choice here. I checked and on Linux arc4random() is provided by libbsd library. OK - problem solved:

#if os(Linux)
    import Glibc
    import SwiftShims
#else
    import Darwin
#endif

func cs_arc4random_uniform(upperBound: UInt32) -> UInt32 {
    #if os(Linux)
        return _swift_stdlib_arc4random_uniform(upperBound)
    #else
        return arc4random_uniform(upperBound)
    #endif
}

at the and, in terms of source code, Swift 2.2 change name of function anyGenerator(..) to AnyGenerator(..) and I had to come with dirty hack for that. Linux work with Swift 2.2 only, OSX works with Swift 2.1 - and I can't find way to conditionally build code against one or another. So I did this dirty hack:

private func CS_AnyGenerator<Element>(body: () -> Element?) -> AnyGenerator<Element> {
#if os(Linux)
    return AnyGenerator(body: body)
#else
    return anyGenerator(body)
#endif
}

just for now.

Swift Package Manager

abbreviation: SPM

It is natural that I want support shiny new Package Manager by Apple so I started reading docs. There is lot of docs but all informations are very basic, too basic for me. I struggled a little with Package.swift and overall design.

As for today I found this tool a little too limited, too much assumptions, but it changing. In particular, to my surprise, I realized that I can't simply build my package because directories layout (by Xcode) is incompatible with SPM. SPM assume that you're code is located inside folder named "Sources", "source", "src", "srcs". Mine was not, mine source code is located, of course in directory named "CryptoSwift".

If you struggle with this issue, here's my solution:

New directory named SwiftPackageManager with subdirectory named Sources and file Package.swift. Inside Source I created symlink named CryptoSwift. Symlink point to my existing directory with source files.

The name of subdirectory is important because is used to determine name of modules - If I create here subfolder named "Foundation" - it is impossible to build the project because of error:

Compiling Swift Module 'Foundation'

SwiftPackageManager/Sources/Foundation/AES+Foundation.swift:9:8: warning: file 'AES+Foundation.swift' is part of module 'Foundation'; ignoring import
import Foundation

that is obviously NOT THAT Foundation framework. So be careful with directories - it's module now.

Package.swift

Package.swift is the manifest file that contains metadata about your package.

after all that I can create Package.swift file - this is metadata file for package. Here is the file:

import PackageDescription

let package = Package(
    name: "CryptoSwift"
)

and build the package

$ swift build
Compiling Swift Module 'CryptoSwift' (32 sources)
Linking Library:  .build/debug/CryptoSwift.a

result is stored in hidden directory .build

Result

CryptoSwift is officially Linux compatible (develop branch) and support (I hope) Swift Package Manager - that is third Package Manager after CocoaPods and Carthage.

I can't use it now

and now the point. I don't know how to use it. I have no idea. Tried build test project with dependency to CryptoSwift package but it is not working (errors, more troubles, "can't find file" etc). Problem is that SPM is trying to be way too smart and ruin everything. If you change folder layout it all blow up. I can't specify branch (it fetched code from master branch o_O). When it get version it rely on tags and of course can't test until create that tag.

I'm almost sure it's because I don't have blessed layout of folders, but hey...

If you can figure out how use my package, please let me know!

Update: I created ticked for Package Manager here: https://bugs.swift.org/browse/SR-29 feel free to participate.

repo: https://github.com/krzyzanowskim/CryptoSwift/tree/develop/SwiftPackageManager