Shimmy Shimmy Ya
With Swift 4.2 the Package Manager introduces System Library Targets.
Being an iOS developer, I cannot fully use the Package Manager yet, but I keep my frameworks compatible if possible, with the nice side effect of making them workable without Xcode.
A shining facet of Swift is its interoperabiltiy with C, but importing a C library into Swift has been surprisingly cumbersome before Swift 4.2.
With system library targets, which move the current system-module packages feature from package to target level, describing a package dependency on a system library became much easier. Previously, we were tempted to create isolated repos for our system library shims, containing specific module maps—undermining the team’s original, remarkably naïve, intention of inherent standardization.
Our original motivation in forcing system packages to be declared as standalone packages was to encourage the ecosystem to standardize on them, their names, their repository locations, and their owners. In practice, this effort did not work out and it only made the package manager harder to use.
Using the new system library target, we can ditch those extra repos, pulling the module maps into our packages, where we are now able to express system library dependencies, without exposing them.
let package = Package( name: "ZLib", products: [ .library(name: "ZLib", targets: ["ZLib"]), ], targets: [ .target( name: "ZLib", dependencies: ["CZLib"]), .systemLibrary( name: "CZLib") ] )
ZLib package depends on
CZLib, a C library, we are now able to make available inside our package.
OK—what? Here’s a concrete example. I’ve learned about this today, while I’ve been updating my Skull framework, the extra thin SQLite wrapper I like to use. This package, of course, depends on
SQLite3, which I had to express using a system-module package, living in its own little repo.
With 4.2, this elegant, turtles all the way down, but totally impractical, approach has been deprecated. Now, I can describe packages with internal system library dependencies using targets. Yaaaay! 🎉
// swift-tools-version:4.2 import PackageDescription let package = Package( name: "Skull", products: [ .library(name: "Skull", targets: ["Skull"]) ], targets: [ .systemLibrary( name: "CSqlite3", path: "Libraries/CSqlite3"), .target( name: "Skull", dependencies: ["CSqlite3"], path: "Sources"), .testTarget( name: "SkullTests", dependencies: ["Skull"]) ], swiftLanguageVersions: [.v4_2] )
And provide the module map for the system library named
CSqlite3 within the package via the
Libraries └── CSqlite3 ├── module.modulemap └── shim.h
It works if, after cloning into Skull, you are able run its tests.
$ swift test
Another thing you might want to try is running the example, which is slightly more interesting, for it’s also resolving dependencies.
$ cd example && swift run
Which should result in something like the following.
Fetching https://github.com/michaelnisi/skull Completed resolution in 1.53s Cloning https://github.com/michaelnisi/skull Resolving https://github.com/michaelnisi/skull at 8.0.2 Compile Swift Module 'Skull' (1 sources) Compile Swift Module 'example' (1 sources) Linking ./.build/x86_64-apple-macosx10.10/debug/example Earth
Where 🌍 comes from the database and means it worked. That’s it, nothing earth-shattering, just a thing I learned today. I hope you like it raw. ✌️🕶