12
12
13
13
import ArgumentParser
14
14
import Foundation
15
+ import RegexBuilder
15
16
16
17
fileprivate let modules : [ String ] = [
17
18
" SwiftParser " ,
@@ -29,11 +30,12 @@ struct VerifySourceCode: ParsableCommand {
29
30
var arguments : SourceCodeGeneratorArguments
30
31
31
32
func run( ) throws {
32
- let executor = VerifySourceCodeExecutor (
33
+ try VerifySpiYmlExecutor ( ) . run ( )
34
+
35
+ try VerifySourceCodeExecutor (
33
36
toolchain: arguments. toolchain,
34
37
verbose: arguments. verbose
35
- )
36
- try executor. run ( )
38
+ ) . run ( )
37
39
}
38
40
}
39
41
@@ -101,3 +103,74 @@ struct VerifySourceCodeExecutor {
101
103
}
102
104
}
103
105
}
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