Recent SwiftPM features and enhancements

This post summarizes some of the features and enhancements that landed in SwiftPM trunk recently. It would be great if the community can try some of these features and possibly provide feedback. These features are available in the latest trunk or Swift 4.2 toolchain. If you find something unexpected, feel free to reply to this post or create a bug report in JIRA.

Improved scheme generation logic:
The scheme generation logic is improved and generates schemes as follows:

  • One scheme containing all regular and test targets of the root package.
  • One scheme per executable target containing the test targets whose dependencies intersect with the dependencies of the exectuable target.

Local dependencies:
This is implementation of SE-0201. You will need to update the tools version comment to 4.2 in order to use this feature.

Known bug: if you convert an existing dependency to a local dependency, you will need to run swift package update or blow away the .build directory in order for the local package to take effect. Fixed by #1616.

Automatic Xcode project generation:
The generate-xcodeproj has a new --watch option to watch the file system and automatically regenerate the Xcode project if needed. This uses the watchman tool to watch the required files. In future, we can add this option to other commands like swift-build, swift-test, swift-run.

System library targets:
This is implementation for SE-0208. You will need to the update tools version comment to 4.2 in order to use this feature.

Embedable Xcode projects:
For various complex reasons, C language targets are not mapped to "real" framework targets in the generated Xcode project. In practice, this means that the "defines module" and "enable modules" build settings are turned off in the generated framework target. This causes issues when trying to use the generated project as a subproject.

The issue is resolved for C language targets that have an umbrella header. To create an umbrella header, place a file named <targetname>.h inside the include/ directory of that target and include all the public headers in the umbrella header.

17 Likes

Yo, this is awesome. Thanks for the update on SPM, Ankit!

Are you using a Swift wrapper of Watchman? How are you talking to it? This could be useful for a number of community projects, like SourceKittenDaemon.

Sorry if I've already asked about this (I might've), but not seeing an example repo there makes me question if I understand how this is used.

If I got it correctly, this allows me to do something like... having a Web Framework that uses Curl as a dependency, which gets imported directly into Swift?

Something like:

  • Target 1: my framework. Depends on Target 2.
  • Target 2: a clang import into Swift, of the libCurl library.

All of this in a single repo.

And also: I don't understand how Target 2 gets reflected in the repo's Source directory. How's that work? Is it all implicit?

1 Like

Currently, there are no bindings. SwiftPM invokes watchman-make command-line tool with the required configuration.

Correct, this will allow you to use system libraries without creating another repository. You're right that we should probably have a real example somewhere. I'll update once I create one!

2 Likes

I will be looking forward to that :blush:

Awesome stuff. I'm very excited for the improved scheme generation. :smiley:

1 Like

I am trying to use the new .systemLibrary target but I don't understand how to use it:

  • It seems that it requires a directory named as the target to be available in the Sources folder. Why is this required?
  • How do you make the system library target available to other targets as a module?

For example:

import PackageDescription

let package = Package(
    name: "PythonKit",
    products: [
        .library(name: "PythonKit", targets: ["PythonKit"]),
    ],
    targets: [
        .target(name: "PythonKit", dependencies: ["Python"], path: "PythonKit"),
        .systemLibrary(name: "Python", path: "Python", pkgConfig: "python"),
    ]
)

Fails to build with the following error:

/Developer/PythonKit/PythonKit/PythonGlue.swift:22:8: error: no such module 'Python'
import Python
       ^

I attached an example package that uses this feature in this bug: [SR-8091] Update documentation for System Library Targets (SE-0208) · Issue #4784 · apple/swift-package-manager · GitHub

Thank you! I was trying something very similar but putting the header and the module map in an include directory instead of directly in the target folder.

(For reference: PythonKit package updated using .systemLibrary.)

1 Like

Shameless plug: swift-watch.

OVERVIEW: Watches over your Swift project's source

Tasks (-x & -s) are executed in the order they appear.

USAGE: swift watch [options]

OPTIONS:
   -c, --clear                   Clear output before each execution
   -d, --dry-run                 Do not run any commands, just print them
   -q, --quiet                   Suppress output from swift-watch itself
   -p, --postpone                Postpone initial execution until the first change
   -m, --monochrome              Suppress coloring of output from swift-watch itself
   -x, --exec=<cmd>              Swift command(s) to execute on changes
   -s, --shell=<cmd>             Shell command(s) to execute on changes
   -h, --help                    The help menu

I built it almost a year ago in an attempt to replicate my Rust workflow (Atom + iTerm2 + cargo-watch), but it became apparent rather quickly that Swift's CLI is not meant to actually be used by humans, productively and efficiently. The output of swift build and swift test is so damn obfuscated with noise and lacking output coloring, that despite having built swift-watch as an almost feature-complete port of my beloved cargo-watch replicating my workflow in Swift just isn't the same. There is juts no joy in searching for signal in all that noise over and over again.

2 Likes

Nice! I know the output of swift build and swift test is not very good right now but it is one of the goals to have great CLI experience, which includes pretty output. We do have pretty output in some commands like swift test --parallel

Thank you, I was looking for good ways in which to describe my experience and this fits the bill perfectly. ;)

I am not sure I understand the following:

I described my situation/understanding in more details in a new post here.

This save my life. Thanks Apple :)

I think SwiftPM's support for C/ObjC/C++ language is still far more way to go. Many corner cases, which can be easily resolved by raw Xcode subproject, or even CocoaPods. However, no luck for SwiftPM current weak DSL sytnax