Parsing Package.swift

As I’m working on my Swift language server, one of the things I need to do is to parse the Package.swift file. There is a PackageDescription library, but that’s unable for use within your own program if you are actually using SwiftPM.


So what are we to do? HACK IT UP!

Basically, we need to run this command:

$ swift -I /Library/Developer/Toolchains/swift-latest.xctoolchain/usr/lib/swift/pm \
    -L /Library/Developer/Toolchains/swift-latest.xctoolchain/usr/lib/swift/pm \
    -lPackageDescription Package.swift -fileno 1

Not really a big deal, just annoying.

Here’s the snippet to get the output:

// TODO(owensd): Need to actually get the version of Swift that the workspace is using based on the OS.
projectPath = params.rootPath!
let includePath = "/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/lib/swift/pm"
let packagePath = "\(projectPath!)/Package.swift"
let swiftPath = "/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin/swift"
let output = shell(
tool: swiftPath,
arguments: ["-I", includePath, "-L", includePath, "-lPackageDescription", packagePath, "-fileno", "1"])
let packageJson = try JSValue.parse(output)
moduleName = packageJson["package"]["name"].string!

view raw
hosted with ❤ by GitHub

Curious note: when you use string literal interpolation, even if your variable is an implicitly unwrapped optional, it will get output as an optional. Hence you see: "\(projectPath!)/package.swift". Go figure.

Of course, the output is JSON. If you need a parser, you can use json-swift or any of the many others.


The shell function is just a helper that I add to my SwiftFixes.swift file:

public func shell(tool toolPath: String, arguments: [String]) -> String {
let process = Process()
process.launchPath = toolPath
process.arguments = arguments
let output = Pipe()
process.standardOutput = output
return String(
data: output.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8)!

view raw
hosted with ❤ by GitHub

Anyhow, if you too need to parse that pesky Package.swift file, here you go.

Parsing Package.swift