Skip to content

Commit a119a27

Browse files
committed
Verify that .spi.yml is up-to-date in swift-syntax-dev-utils
.spi.yml at one point got out of sync of the libraries we added to Package.swift. Add a verification script to make sure that they match. The idea is that for every library that every library that is exposed by SwiftSyntax should be documentated on swiftpackageindex.com. rdar://108901461
1 parent 44f6968 commit a119a27

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

SwiftSyntaxDevUtils/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import PackageDescription
66
let package = Package(
77
name: "swift-syntax-dev-utils",
88
platforms: [
9-
.macOS(.v10_15)
9+
.macOS(.v13)
1010
],
1111
products: [
1212
.executable(name: "swift-syntax-dev-utils", targets: ["swift-syntax-dev-utils"])

SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/VerifySourceCode.swift

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ArgumentParser
1414
import Foundation
15+
import RegexBuilder
1516

1617
fileprivate let modules: [String] = [
1718
"SwiftParser",
@@ -29,11 +30,12 @@ struct VerifySourceCode: ParsableCommand {
2930
var arguments: SourceCodeGeneratorArguments
3031

3132
func run() throws {
32-
let executor = VerifySourceCodeExecutor(
33+
try VerifySpiYmlExecutor().run()
34+
35+
try VerifySourceCodeExecutor(
3336
toolchain: arguments.toolchain,
3437
verbose: arguments.verbose
35-
)
36-
try executor.run()
38+
).run()
3739
}
3840
}
3941

@@ -101,3 +103,74 @@ struct VerifySourceCodeExecutor {
101103
}
102104
}
103105
}
106+
107+
struct VerifySpiYmlExecutor {
108+
static let configuration = CommandConfiguration(
109+
abstract: "Verify that the .spi.yml file contains all libraries from Package.swift"
110+
)
111+
112+
/// Returns all libraries declared in `Package.swift`.
113+
///
114+
/// Note: It would be nice if we could compile Package.swift with this file and reallly
115+
/// inspect the package targets instead of doing regex scraping, but this is good enough
116+
/// for now.
117+
private func librariesInPackageManifest() throws -> [String] {
118+
let extractNameRegex = Regex {
119+
#/^.*/#
120+
#".library(name: ""#
121+
Capture(ZeroOrMore(.word))
122+
#"""#
123+
#/.*$/#
124+
}
125+
let packageFile = Paths.packageDir.appendingPathComponent("Package.swift")
126+
let packageFileContents = try String(contentsOf: packageFile)
127+
return
128+
packageFileContents
129+
.components(separatedBy: "\n")
130+
.filter({ !$0.matches(of: extractNameRegex).isEmpty })
131+
.map { $0.replacing(extractNameRegex) { $0.1 } }
132+
.sorted()
133+
}
134+
/// Returns all targets listed in `.spi.yml`.
135+
///
136+
/// Note: It would be nice to actually parse the .yml file but then we would need to add
137+
/// a dependency from this script on a YAML parser and that just doesn’t seem worth it.
138+
private func targetsInSwiftPackageIndexManifest() throws -> [String] {
139+
let extractTargetRegex = Regex {
140+
#/^ - /#
141+
Capture(ZeroOrMore(.word))
142+
#/$/#
143+
}
144+
let spiYmlFile = Paths.packageDir.appendingPathComponent(".spi.yml")
145+
let spiYmlFileContents = try String(contentsOf: spiYmlFile)
146+
return
147+
spiYmlFileContents
148+
.components(separatedBy: "\n")
149+
.filter({ !$0.matches(of: extractTargetRegex).isEmpty })
150+
.map { $0.replacing(extractTargetRegex) { $0.1 } }
151+
.sorted()
152+
}
153+
154+
func run() throws {
155+
logSection("Verifing that .spi.yml is up-to-date")
156+
157+
let difference = try targetsInSwiftPackageIndexManifest().difference(from: librariesInPackageManifest())
158+
159+
if !difference.isEmpty {
160+
let differenceDescription = difference.map { change in
161+
switch change {
162+
case .insert(_, let element, _):
163+
return " - Unexpected in .spi.yml: \(element)"
164+
case .remove(_, let element, _):
165+
return " - Missing in .spi.yml: \(element)"
166+
}
167+
}.joined(separator: "\n")
168+
throw ScriptExectutionError(
169+
message: """
170+
.spi.yml did not contain the same libraries as Package.swift:
171+
\(differenceDescription)
172+
"""
173+
)
174+
}
175+
}
176+
}

0 commit comments

Comments
 (0)