Skip to content

Commit fa430cd

Browse files
committed
Add a script to verify that .spi.yml is up-to-date
.spi.yml 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. This is also a first stab at writing part of the build infrastructure scripts in Swift. rdar://108901461
1 parent 427d59d commit fa430cd

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

build-script.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,18 @@ def verify_source_code_command(args: argparse.Namespace) -> None:
486486
)
487487
raise SystemExit(1)
488488

489+
try:
490+
swift = os.path.join(args.toolchain, 'bin', 'swift')
491+
verify_spi_yml_script = os.path.join(PACKAGE_DIR, 'utils', 'verifySpiYml.swift')
492+
check_call([swift, verify_spi_yml_script], verbose=args.verbose)
493+
494+
except subprocess.CalledProcessError:
495+
printerr(
496+
"FAIL: Targets specified in .spi.yml did not match the libraries " +
497+
"declared in Package.swift. Please update .spi.yml"
498+
)
499+
raise SystemExit(1)
500+
489501

490502
def build_command(args: argparse.Namespace) -> None:
491503
try:

utils/verifySpiYml.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Foundation
2+
import RegexBuilder
3+
4+
/// The package root that contains `Package.swift`, `Sources` etc.
5+
var packageRoot: URL {
6+
return URL(fileURLWithPath: #filePath)
7+
.deletingLastPathComponent()
8+
.deletingLastPathComponent()
9+
}
10+
11+
/// Returns all libraries declared in `Package.swift`.
12+
///
13+
/// Note: It would be nice if we could compile Package.swift with this file and reallly
14+
/// inspect the package targets instead of doing regex scraping, but this is good enough
15+
/// for now.
16+
func librariesInPackageManifest() throws -> [String] {
17+
let extractNameRegex = Regex {
18+
#/^.*/#
19+
#".library(name: ""#
20+
Capture(ZeroOrMore(.word))
21+
#"""#
22+
#/.*$/#
23+
}
24+
25+
let packageFile = packageRoot.appending(component: "Package.swift")
26+
let packageFileContents = try String(contentsOf: packageFile)
27+
28+
return
29+
packageFileContents
30+
.components(separatedBy: "\n")
31+
.filter({ !$0.matches(of: extractNameRegex).isEmpty })
32+
.map { $0.replacing(extractNameRegex) { $0.1 } }
33+
.sorted()
34+
}
35+
36+
/// Returns all targets listed in `.spi.yml`.
37+
///
38+
/// Note: It would be nice to actually parse the .yml file but then we would need to add
39+
/// a dependency from this script on a YAML parser and that just doesn’t seem worth it.
40+
func targetsInSwiftPackageIndexManifest() throws -> [String] {
41+
let extractTargetRegex = Regex {
42+
#/^ - /#
43+
Capture(ZeroOrMore(.word))
44+
#/$/#
45+
}
46+
47+
let spiYmlFile = packageRoot.appending(component: ".spi.yml")
48+
let spiYmlFileContents = try String(contentsOf: spiYmlFile)
49+
50+
return
51+
spiYmlFileContents
52+
.components(separatedBy: "\n")
53+
.filter({ !$0.matches(of: extractTargetRegex).isEmpty })
54+
.map { $0.replacing(extractTargetRegex) { $0.1 } }
55+
.sorted()
56+
}
57+
58+
let difference = try targetsInSwiftPackageIndexManifest().difference(from: librariesInPackageManifest())
59+
60+
for change in difference {
61+
switch change {
62+
case .insert(_, let element, _):
63+
print("Unexpected in .spi.yml: \(element)")
64+
case .remove(_, let element, _):
65+
print("Missing in .spi.yml: \(element)")
66+
}
67+
}
68+
69+
exit(difference.isEmpty ? 0 : 1)

0 commit comments

Comments
 (0)