Skip to content

Commit

Permalink
Add SPM config support (#1184)
Browse files Browse the repository at this point in the history
* Work on adding SPM support in configuration files

* Change configuration setup, setup watch paths

* Fix custom path in Package and relate excluded to right directory

* Update docs and add CHANGELOG.md entry
  • Loading branch information
jimmya authored Jul 28, 2023
1 parent 6db4d41 commit 51290a5
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Sourcery CHANGELOG

## 2.1.0
## Changes
- Added support for Swift Package Manager config ([#1184](https://github.com/krzysztofzablocki/Sourcery/pull/1184))

## 2.0.3
## Internal Changes
- Modifications to included files of Swift Templates are now detected by hashing instead of using the modification date when invalidating the cache ([#1161](https://github.com/krzysztofzablocki/Sourcery/pull/1161))
Expand Down
71 changes: 71 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,59 @@
"version" : "2.10.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "e394bf350e38cb100b6bc4172834770ede1b7232",
"version" : "1.0.3"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
"version" : "1.0.4"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "75ec60b8b4cc0f085c3ac414f3dca5625fa3588e",
"version" : "2.2.4"
}
},
{
"identity" : "swift-driver",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-driver.git",
"state" : {
"branch" : "release/5.8",
"revision" : "7cfe0c0b6e6297efe88a3ce34e6138ee7eda969e"
}
},
{
"identity" : "swift-llbuild",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-llbuild.git",
"state" : {
"branch" : "release/5.8",
"revision" : "168f9dc3798def1ecdd7d40049f6e1841bf761d0"
}
},
{
"identity" : "swift-package-manager",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-package-manager",
"state" : {
"revision" : "fa3db13e0bd00e33c187c63c80673b3ac7c82f55"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
Expand All @@ -126,6 +179,24 @@
"version" : "508.0.0"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "836bc4557b74fe6d2660218d56e3ce96aff76574",
"version" : "1.1.1"
}
},
{
"identity" : "swift-tools-support-core",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-tools-support-core.git",
"state" : {
"branch" : "release/5.8",
"revision" : "ac4871e01ef338cb95b5d28328cab0ec1dfae935"
}
},
{
"identity" : "xcodeproj",
"kind" : "remoteSourceControl",
Expand Down
8 changes: 5 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
let package = Package(
name: "Sourcery",
platforms: [
.macOS(.v10_15),
.macOS(.v12),
],
products: [
// SPM won't generate .swiftmodule for a target directly used by a product,
Expand All @@ -29,7 +29,8 @@ let package = Package(
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.3.1"),
.package(url: "https://github.com/apple/swift-syntax.git", from: "508.0.0"),
.package(url: "https://github.com/Quick/Quick.git", from: "3.0.0"),
.package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0")
.package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"),
.package(url: "https://github.com/apple/swift-package-manager", revision: "fa3db13e0bd00e33c187c63c80673b3ac7c82f55"),
],
targets: [
.executableTarget(
Expand All @@ -55,7 +56,8 @@ let package = Package(
"StencilSwiftKit",
.product(name: "SwiftSyntax", package: "swift-syntax"),
"XcodeProj",
"TryCatch"
"TryCatch",
.product(name: "SwiftPM-auto", package: "swift-package-manager"),
],
path: "Sourcery",
exclude: [
Expand Down
66 changes: 65 additions & 1 deletion Sourcery/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import PathKit
import Yams
import SourceryRuntime
import QuartzCore
import Basics
import TSCBasic
import Workspace
import PackageModel

public struct Project {
public let file: XcodeProj
Expand Down Expand Up @@ -150,9 +154,64 @@ extension Path {
}
}

public struct Package {
public let root: Path
public let targets: [Target]

public struct Target {
let name: String
let root: Path
let excludes: [Path]
}

public init(dict: [String: Any], relativePath: Path) throws {
guard let packageRootPath = dict["path"] as? String else {
throw Configuration.Error.invalidSources(message: "Package file directory path is not provided. Expected string.")
}
let path = Path(packageRootPath, relativeTo: relativePath)

let packagePath = try AbsolutePath(validating: path.string)
let observability = ObservabilitySystem { Log.verbose("\($0): \($1)") }
let workspace = try Workspace(forRootPackage: packagePath)

var manifestResult: Result<Manifest, Error>?
let semaphore = DispatchSemaphore(value: 0)
workspace.loadRootManifest(at: packagePath, observabilityScope: observability.topScope, completion: { result in
manifestResult = result
semaphore.signal()
})
semaphore.wait()

guard let manifest = try manifestResult?.get() else{
throw Configuration.Error.invalidSources(message: "Unable to load manifest")
}
self.root = path
let targetNames: [String]
if let targets = dict["target"] as? [String] {
targetNames = targets
} else if let target = dict["target"] as? String {
targetNames = [target]
} else {
throw Configuration.Error.invalidSources(message: "'target' key is missing. Expected object or array of objects.")
}
let sourcesPath = Path("Sources", relativeTo: path)
self.targets = manifest.targets.compactMap({ target in
guard targetNames.contains(target.name) else {
return nil
}
let rootPath = target.path.map { Path($0, relativeTo: path) } ?? Path(target.name, relativeTo: sourcesPath)
let excludePaths = target.exclude.map { path in
Path(path, relativeTo: rootPath)
}
return Target(name: target.name, root: rootPath, excludes: excludePaths)
})
}
}

public enum Source {
case projects([Project])
case sources(Paths)
case packages([Package])

public init(dict: [String: Any], relativePath: Path) throws {
if let projects = (dict["project"] as? [[String: Any]]) ?? (dict["project"] as? [String: Any]).map({ [$0] }) {
Expand All @@ -164,8 +223,11 @@ public enum Source {
} catch {
throw Configuration.Error.invalidSources(message: "\(error)")
}
} else if let packages = (dict["package"] as? [[String: Any]]) ?? (dict["package"] as? [String: Any]).map({ [$0] }) {
guard !packages.isEmpty else { throw Configuration.Error.invalidSources(message: "No packages provided.") }
self = try .packages(packages.map({ try Package(dict: $0, relativePath: relativePath) }))
} else {
throw Configuration.Error.invalidSources(message: "'sources' or 'project' key are missing.")
throw Configuration.Error.invalidSources(message: "'sources', 'project' or 'package' key are missing.")
}
}

Expand All @@ -175,6 +237,8 @@ public enum Source {
return paths.allPaths.isEmpty
case let .projects(projects):
return projects.isEmpty
case let .packages(packages):
return packages.isEmpty
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions Sourcery/Sourcery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ public class Sourcery {
case let .sources(paths):
watchPaths = paths
case let .projects(projects):
watchPaths = Paths(include: projects.map({ $0.root }),
exclude: projects.flatMap({ $0.exclude }))
watchPaths = Paths(include: projects.map(\.root),
exclude: projects.flatMap(\.exclude))
case let .packages(packages):
watchPaths = Paths(include: packages.flatMap({ $0.targets.map(\.root) }),
exclude: packages.flatMap({ $0.targets.flatMap(\.excludes) }))
}

let process: (Source) throws -> ParsingResult = { source in
Expand Down Expand Up @@ -106,6 +109,11 @@ public class Sourcery {
}
}
result = try self.parse(from: paths, forceParse: forceParse, parseDocumentation: parseDocumentation, modules: modules, requiresFileParserCopy: hasSwiftTemplates)
case let .packages(packages):
let paths: [Path] = packages.flatMap({ $0.targets.map(\.root) })
let excludePaths: [Path] = packages.flatMap({ $0.targets.flatMap(\.excludes) })
let modules = packages.flatMap({ $0.targets.map(\.name) })
result = try self.parse(from: paths, exclude: excludePaths, forceParse: forceParse, parseDocumentation: parseDocumentation, modules: modules, requiresFileParserCopy: hasSwiftTemplates)
}

try self.generate(source: source, templatePaths: templatesPaths, output: output, parsingResult: &result, forceParse: forceParse, baseIndentation: baseIndentation)
Expand Down
2 changes: 1 addition & 1 deletion SourceryTests/ConfigurationSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ConfigurationSpec: QuickSpec {

it("throws error on missing sources") {
let config: [String: Any] = ["templates": ["."], "output": "."]
expect(configError(config)).to(equal("Invalid sources. 'sources' or 'project' key are missing."))
expect(configError(config)).to(equal("Invalid sources. 'sources', 'project' or 'package' key are missing."))
}

it("throws error on invalid sources format") {
Expand Down
16 changes: 16 additions & 0 deletions guides/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ project:
- <path to xcframework file>
```

You can also provide a Swift Package which will be scanned. Source files will be scanned based on the package's `path` and `exclude` options.

```yaml
package:
path: <path to to the Package.swift root directory>
target: <target name>
```
Multiple targets:
```yaml
package:
path: <path to to the Package.swift root directory>
target:
- <target name>
- <target name>
```

#### Excluding sources or templates

You can specify paths to sources files that should be scanned using `include` key and paths that should be excluded using `exclude` key. These can be directory or file paths.
Expand Down

0 comments on commit 51290a5

Please sign in to comment.