Slide 1

Slide 1 text

Swift Package Manager Tokyo iOS Meetup, March 2016 Boris Bügling - @NeoNacho

Slide 2

Slide 2 text

CocoaPods

Slide 3

Slide 3 text

Contentful

Slide 4

Slide 4 text

Agenda • What is swiftpm? • Making our own package • How does it work? • Comparison with related tools

Slide 5

Slide 5 text

What is swiftpm?

Slide 6

Slide 6 text

import PackageDescription let package = Package( name: "Hello", dependencies: [ .Package(url: "ssh://[email protected]/Greeter.git", versions: Version(1,0,0)..

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

What does it do? • Compiles and links Swift packages • Resolves, fetches and builds their dependencies

Slide 9

Slide 9 text

Current state • Currently builds static libraries or binaries • Supported platforms are OS X and Ubuntu Linux • Only builds Swift code, no C/C++/Objective-C/...

Slide 10

Slide 10 text

$ swift build --help OVERVIEW: Build sources into binary products USAGE: swift build [options] MODES: --configuration Build with configuration (debug|release) [-c] --clean Delete all build intermediaries and products [-k] OPTIONS: --chdir Change working directory before any other operation [-C] -v Increase verbosity of informational output

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Making our own package

Slide 13

Slide 13 text

! A small library for parsing and writing ISO8601 date strings.

Slide 14

Slide 14 text

Sources/ └── Clock ├── ISO8601Parser.swift └── ISO8601Writer.swift 1 directory, 2 files

Slide 15

Slide 15 text

$ touch Package.swift $ swift build

Slide 16

Slide 16 text

Tests?

Slide 17

Slide 17 text

Spectre describe("a person") { let person = Person(name: "Kyle") $0.it("has a name") { try expect(person.name) == "Kyle" } $0.it("returns the name as description") { try expect(person.description) == "Kyle" } }

Slide 18

Slide 18 text

spectre-build $ swift build $ .build/debug/spectre-build -> a person -> has a name -> returns the name as description 2 passes and 0 failures

Slide 19

Slide 19 text

import PackageDescription let package = Package( name: "Clock", testDependencies: [ .Package(url: "https://github.com/neonichu/spectre-build.git", majorVersion: 0), ] )

Slide 20

Slide 20 text

BUILD_DIR=./.build/debug .PHONY: clean lib test test: lib $(BUILD_DIR)/spectre-build clean: swift build --clean lib: swift build

Slide 21

Slide 21 text

$ make test swift build ./.build/debug/spectre-build -> Converting dates to strings -> can convert NSDate to an ISO8601 GMT string -> Parsing of localtime dates -> can parse dates -> can parse dates with negative timezone offsets -> can parse timezone offsets without colons -> Parsing of UTC dates -> can parse dates -> can parse epoch -> can parse dates without seconds -> is resilient against Y2K bugs 8 passes and 0 failures

Slide 22

Slide 22 text

swiftpm testing support swift test => https://t.co/QyfUQR9pdK

Slide 23

Slide 23 text

Swift versions $ cat .swift-version swift-2.2-SNAPSHOT-2015-12-22-a • Is read by either chswift or swiftenv

Slide 24

Slide 24 text

Travis CI os: - linux - osx language: generic sudo: required dist: trusty osx_image: xcode7.2 install: - curl -sL https://gist.github.com/kylef/ 5c0475ff02b7c7671d2a/raw/ 621ef9b29bbb852fdfd2e10ed147b321d792c1e4/swiftenv-install.sh | bash script: - . ~/.swiftenv/init

Slide 25

Slide 25 text

Git tagging • Package.swift only supports tagged dependencies • Don't forget to push your tags to GitHub

Slide 26

Slide 26 text

CocoaPods • chocolat-cli converts Package.swift to a JSON Podspec

Slide 27

Slide 27 text

public func parse_package(packagePath: String) throws -> PackageDescription.Package { // FIXME: We depend on `chswift` installation and use here let toolchainPath = PathKit.Path(POSIX.getenv("CHSWIFT_TOOLCHAIN") ?? "") libc.setenv("SPM_INSTALL_PATH", toolchainPath.parent().description, 1) print_if("Using libPath \(Resources.runtimeLibPath)", false) let package = (try Manifest(path: packagePath)).package print_if("Converting package \(package.name) at \(packagePath)", false) return package }

Slide 28

Slide 28 text

Side-note about Linux

Slide 29

Slide 29 text

Foundation is incomplete and sometimes different from OS X: #if os(Linux) let index = p.startIndex.distanceTo(p.startIndex.successor()) path = NSString(string: p).substringFromIndex(index) #else path = p.substringFromIndex(p.startIndex.successor()) #endif

Slide 30

Slide 30 text

Some things in the standard library might not be available: #if _runtime(_ObjC) // Excluded due to use of dynamic casting and Builtin.autorelease, neither // of which correctly work without the ObjC Runtime right now. // See rdar://problem/18801510 [...] public func getVaList(args: [CVarArgType]) -> CVaListPointer {

Slide 31

Slide 31 text

OS X libc and Glibc can differ: let flags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK if system_glob(cPattern, flags, nil, >) == 0 { #if os(Linux) let matchc = gt.gl_pathc #else let matchc = gt.gl_matchc #endif

Slide 32

Slide 32 text

And other random fun: ./.build/debug/spectre-build /usr/bin/ld: .build/debug/Clock.a(ISO8601Parser.swift.o): undefined reference to symbol '_swift_FORCE_LOAD_$_swiftGlibc' /home/travis/.swiftenv/versions/swift-2.2-SNAPSHOT-2015-12-22-a/ usr/lib/swift/linux/libswiftGlibc.so: error adding symbols: DSO missing from command line clang: error: linker command failed with exit code 1 (use -v to see invocation)

Slide 33

Slide 33 text

Integrate system libraries • Empty Package.swift • module.modulemap: module curl [system] { header "/usr/include/curl/curl.h" link "curl" export * }

Slide 34

Slide 34 text

let package = Package( name: "example", dependencies: [ .Package(url: "https://github.com/neonichu/curl", majorVersion: 1) ] )

Slide 35

Slide 35 text

How does it work?

Slide 36

Slide 36 text

Modules inside SwiftPackageManager • OS abstractions: libc, POSIX, sys • Package: PackageDescription • Manifest: dep • Downloading code: swift-get • Building code: swift-build

Slide 37

Slide 37 text

Dependencies • Can be local or remote Git repositories • Need to be tagged • Will be fetched to ./Packages/MyPackage-0.0.1

Slide 38

Slide 38 text

Rough build process • PackageDescription generates TOML • dep parses the TOML and can generate YAML • Dependencies are fetched by swift-get • YAML is used as input to llbuild • swift-build calls out to llbuild

Slide 39

Slide 39 text

llbuild

Slide 40

Slide 40 text

$ cat .build/debug/Clock.o/llbuild.yaml client: name: swift-build tools: {} targets: "": [] Clock: []

Slide 41

Slide 41 text

commands: : tool: swift-compiler executable: "/usr/bin/swiftc" inputs: ["ISO8601Parser.swift","ISO8601Writer.swift"] outputs: ["","Clock.swiftmodule", "ISO8601Parser.swift.o","ISO8601Writer.swift.o"] module-name: "Clock" module-output-path: "Clock.swiftmodule" is-library: true sources: ["ISO8601Parser.swift","ISO8601Writer.swift"] objects: ["ISO8601Parser.swift.o","ISO8601Writer.swift.o"] import-paths: ["/Users/boris/Projects/Clock/.build/debug"] temps-path: "/Users/boris/Projects/Clock/.build/debug/Clock.o/Clock" other-args: ["-j8","-Onone","-g","-target","x86_64-apple-macosx10.10", "-enable-testing","-sdk", "/.../Developer/SDKs/MacOSX10.11.sdk","-I","/usr/local/include"]

Slide 42

Slide 42 text

: tool: shell inputs: ["","ISO8601Parser.swift.o","ISO8601Writer.swift.o"] outputs: ["","Clock.a"] args: ["/bin/sh","-c","rm -f 'Clock.a'; ar cr 'Clock.a' 'ISO8601Parser.swift.o' 'ISO8601Writer.swift.o'"] description: "Linking Library: .build/debug/Clock.a"

Slide 43

Slide 43 text

Additional Package.swift syntax

Slide 44

Slide 44 text

Targets import PackageDescription let package = Package( name: "Example", targets: [ Target( name: "top", dependencies: [.Target(name: "bottom")]), Target( name: "bottom") ] )

Slide 45

Slide 45 text

Exclusion let package = Package( name: "Example", exclude: ["tools", "docs", "Sources/libA/images"] )

Slide 46

Slide 46 text

Test dependencies import PackageDescription let package = Package( name: "Hello", testDependencies: [ .Package(url: "ssh://[email protected]/Tester.git", versions: Version(1,0,0)..

Slide 47

Slide 47 text

Customizing builds import PackageDescription var package = Package() #if os(Linux) let target = Target(name: "LinuxSources/foo") package.targets.append(target) #endif

Slide 48

Slide 48 text

Comparison with related tools

Slide 49

Slide 49 text

CocoaPods • Centralized discovery • Xcode integration • Support for other languages • Additional metadata in the Manifest • Does not cover the build process

Slide 50

Slide 50 text

Carthage • No manifest for packages • Xcode integration • Support for other languages

Slide 51

Slide 51 text

Support all three • Package.swift • .podspec • .xcodeproj / .xcworkspace

Slide 52

Slide 52 text

!

Slide 53

Slide 53 text

You should think of it as an alpha code base that hasn't had a release yet. Yes, it is useful for doing some things [...] — Daniel Dunbar

Slide 54

Slide 54 text

References • https://swift.org • https://github.com/apple/swift-package-manager • https://github.com/apple/llbuild • https://github.com/neonichu/chocolat • https://github.com/neonichu/chswift • https://github.com/neonichu/freedom • https://github.com/kylef/spectre-build

Slide 55

Slide 55 text

Thank you!

Slide 56

Slide 56 text

@NeoNacho [email protected] http://buegling.com/talks