$30 off During Our Annual Pro Sale. View Details »

Swift Package Manager

Swift Package Manager

This talk is a short introduction into the Swift Package Manager.

Boris Bügling

January 18, 2016
Tweet

More Decks by Boris Bügling

Other Decks in Programming

Transcript

  1. Swift Package Manager
    swift.berlin #14, January 2016
    Boris Bügling - @NeoNacho

    View Slide

  2. CocoaPods

    View Slide

  3. Contentful

    View Slide

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

    View Slide

  5. What is swiftpm?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. 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/...

    View Slide

  10. $ 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

    View Slide

  11. View Slide

  12. Making our own
    package

    View Slide

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

    View Slide

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

    View Slide

  15. $ touch Package.swift
    $ swift build

    View Slide

  16. Tests?

    View Slide

  17. 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"
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. $ 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

    View Slide

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

    View Slide

  23. 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

    View Slide

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

    View Slide

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

    View Slide

  26. 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
    }

    View Slide

  27. Side-note about Linux

    View Slide

  28. 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

    View Slide

  29. 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 {

    View Slide

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

    View Slide

  31. 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)

    View Slide

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

    View Slide

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

    View Slide

  34. How does it work?

    View Slide

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

    View Slide

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

    View Slide

  37. 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

    View Slide

  38. llbuild

    View Slide

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

    View Slide

  40. 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"]

    View Slide

  41. :
    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"

    View Slide

  42. Additional
    Package.swift syntax

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  47. Comparison with
    related tools

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. !

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. Thank you!

    View Slide

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

    View Slide