Skip to content

Commit 474ae9c

Browse files
committed
Migrate generate-pipeline to a SwiftPM build tool plug-in.
The biggest change that isn't just the plug-in use itself is how we detect opt-in rules now. The previous version "cheated" by linking the rules themselves into `generate-pipeline` and then used some dynamic class lookup to get the actual value at runtime. This was replaced with a syntax tree scan that looks for a simple `true` or `false` return value in a static/class property named `isOptIn`. (A future version of swift-format should probably use compile-time constants for this property, when that's officially supported.)
1 parent 3b71f4c commit 474ae9c

File tree

11 files changed

+268
-527
lines changed

11 files changed

+268
-527
lines changed

Documentation/Development.md

-26
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
11
# Developing `swift-format`
22

3-
## Keeping the Pipeline and Tests Updated
4-
5-
Since Swift does not yet have a runtime reflection system, we use code
6-
generation to keep the linting/formatting pipeline up-to-date. If you add or
7-
remove any rules from the `SwiftFormatRules` module, or if you add or remove
8-
any `visit` methods from an existing rule in that module, you must run the
9-
`generate-pipeline` tool update the pipeline and configuration sources.
10-
11-
The easiest way to do this is to run the following command in your terminal:
12-
13-
```shell
14-
swift run generate-pipeline
15-
```
16-
17-
If successful, this tool will update
18-
`Sources/SwiftFormatConfiguration/RuleRegistry+Generated.swift` and
19-
`Sources/SwiftFormat/Pipelines+Generated.swift`.
20-
21-
Likewise, you should keep the Linux XCTest manifests updated if you add or
22-
remove any tests from `swift-format` by running the following command in your
23-
terminal:
24-
25-
```shell
26-
swift test --generate-linuxmain
27-
```
28-
293
## Command Line Options for Debugging
304

315
`swift-format` provides some hidden command line options to facilitate

Package.swift

+15-5
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ let package = Package(
4848
.product(name: "SwiftSyntax", package: "swift-syntax"),
4949
.product(name: "SwiftOperators", package: "swift-syntax"),
5050
.product(name: "SwiftParser", package: "swift-syntax"),
51-
]
51+
],
52+
plugins: ["generate-pipeline-plugin"]
5253
),
5354
.target(
54-
name: "SwiftFormatConfiguration"
55+
name: "SwiftFormatConfiguration",
56+
plugins: ["generate-pipeline-plugin"]
5557
),
5658
.target(
5759
name: "SwiftFormatCore",
@@ -63,7 +65,8 @@ let package = Package(
6365
),
6466
.target(
6567
name: "SwiftFormatRules",
66-
dependencies: ["SwiftFormatCore", "SwiftFormatConfiguration"]
68+
dependencies: ["SwiftFormatCore", "SwiftFormatConfiguration"],
69+
plugins: ["generate-pipeline-plugin"]
6770
),
6871
.target(
6972
name: "SwiftFormatPrettyPrint",
@@ -90,15 +93,22 @@ let package = Package(
9093
]
9194
),
9295

96+
.plugin(
97+
name: "generate-pipeline-plugin",
98+
capability: .buildTool(),
99+
dependencies: [
100+
"generate-pipeline"
101+
]
102+
),
93103
.executableTarget(
94104
name: "generate-pipeline",
95105
dependencies: [
96-
"SwiftFormatCore",
97-
"SwiftFormatRules",
106+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
98107
.product(name: "SwiftSyntax", package: "swift-syntax"),
99108
.product(name: "SwiftParser", package: "swift-syntax"),
100109
]
101110
),
111+
102112
.executableTarget(
103113
name: "swift-format",
104114
dependencies: [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import PackagePlugin
14+
15+
/// The name of the Swift file to generate for a particular target.
16+
let targetGeneratedSourceMapping = [
17+
"SwiftFormat": "Pipelines+Generated.swift",
18+
"SwiftFormatConfiguration": "RuleRegistry+Generated.swift",
19+
"SwiftFormatRules": "RuleNameCache+Generated.swift",
20+
]
21+
22+
/// A Swift Package Manager build tool that runs `generate-pipeline` to generate the format/lint
23+
/// pipelines from the current state of the rules.
24+
@main
25+
struct GeneratePipelinePlugin: BuildToolPlugin {
26+
enum Error: Swift.Error, CustomStringConvertible {
27+
/// The plugin was applied to a target that isn't supported.
28+
case notApplicableToTarget(String)
29+
30+
var description: String {
31+
switch self {
32+
case .notApplicableToTarget(let target):
33+
return """
34+
'generate-pipeline-plugin' cannot be applied to '\(target)'; \
35+
supported targets are \(targetGeneratedSourceMapping.keys)
36+
"""
37+
}
38+
}
39+
}
40+
41+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
42+
guard let generatedSourceName = targetGeneratedSourceMapping[target.name] else {
43+
throw Error.notApplicableToTarget(target.name)
44+
}
45+
46+
let generatePipelineTool = try context.tool(named: "generate-pipeline")
47+
let sourcesDir = context.package.directory.appending("Sources")
48+
let outputFile = context.pluginWorkDirectory
49+
.appending("GeneratedSources")
50+
.appending(generatedSourceName)
51+
52+
return [
53+
.buildCommand(
54+
displayName: "Generating \(generatedSourceName) for \(target.name)",
55+
executable: generatePipelineTool.path,
56+
arguments: [
57+
"--sources-directory",
58+
sourcesDir.string,
59+
"--output-file",
60+
outputFile.string,
61+
"--target",
62+
target.name,
63+
],
64+
outputFiles: [outputFile]
65+
)
66+
]
67+
}
68+
}

0 commit comments

Comments
 (0)