@@ -25,19 +25,20 @@ public enum CocoaPodUtils {
25
25
/// Public name of the pod.
26
26
let name : String
27
27
28
- /// The version of the pod.
28
+ /// The version of the requested pod.
29
29
let version : String ?
30
30
}
31
31
32
32
/// Information associated with an installed pod.
33
33
public struct PodInfo {
34
- let versionedPod : VersionedPod
34
+ /// The version of the generated pod.
35
+ let version : String
35
36
36
- /// The location of the pod on disk .
37
- var installedLocation : URL
37
+ /// The pod dependencies .
38
+ let dependencies : [ String ]
38
39
39
- var name : String { return versionedPod . name }
40
- var version : String { return versionedPod . version ?? " " }
40
+ /// The location of the pod on disk.
41
+ let installedLocation : URL
41
42
}
42
43
43
44
/// Executes the `pod cache clean --all` command to remove any cached CocoaPods.
@@ -54,23 +55,8 @@ public enum CocoaPodUtils {
54
55
}
55
56
}
56
57
57
- /// Executes the `pod cache list` command to get the Pods curerntly cached on your machine.
58
- ///
59
- /// - Parameter dir: The directory containing all installed pods.
60
- /// - Returns: A dictionary keyed by the pod name, then by version number.
61
- public static func listPodCache( inDir dir: URL ) -> [ String : [ String : PodInfo ] ] {
62
- let result = Shell . executeCommandFromScript ( " pod cache list " , outputToConsole: false )
63
- switch result {
64
- case let . error( code) :
65
- fatalError ( " Could not list the pod cache in \( dir) , the command exited with \( code) . Try " +
66
- " running in Terminal to see what's wrong. " )
67
- case let . success( output) :
68
- return parsePodsCache ( output: output. components ( separatedBy: " \n " ) )
69
- }
70
- }
71
-
72
58
/// Gets metadata from installed Pods. Reads the `Podfile.lock` file and parses it.
73
- public static func installedPodsInfo( inProjectDir projectDir: URL ) -> [ PodInfo ] {
59
+ public static func installedPodsInfo( inProjectDir projectDir: URL ) -> [ String : PodInfo ] {
74
60
// Read from the Podfile.lock to get the installed versions and names.
75
61
let podfileLock : String
76
62
do {
@@ -80,33 +66,21 @@ public enum CocoaPodUtils {
80
66
" \( projectDir) : \( error) " )
81
67
}
82
68
83
- // Get the versions in the format of [PodName: VersionString].
84
- let versions = loadVersionsFromPodfileLock ( contents: podfileLock)
85
-
86
- // Generate an InstalledPod for each Pod found.
87
- let podsDir = projectDir. appendingPathComponent ( " Pods " )
88
- var installedPods : [ PodInfo ] = [ ]
89
- for (podName, version) in versions {
90
- let podDir = podsDir. appendingPathComponent ( podName)
91
- guard FileManager . default. directoryExists ( at: podDir) else {
92
- fatalError ( " Directory for \( podName) doesn't exist at \( podDir) - failed while getting " +
93
- " information for installed Pods. " )
94
- }
95
-
96
- let podInfo = PodInfo ( versionedPod: VersionedPod ( name: podName, version: version) ,
97
- installedLocation: podDir)
98
- installedPods. append ( podInfo)
99
- }
100
-
101
- return installedPods
69
+ // Get the pods in the format of [PodInfo].
70
+ return loadPodInfoFromPodfileLock ( contents: podfileLock)
102
71
}
103
72
104
- /// Install an array of pods in a specific directory, returning an array of PodInfo for each pod
73
+ /// Install an array of pods in a specific directory, returning a dictionary of PodInfo for each pod
105
74
/// that was installed.
75
+ /// - Parameters:
76
+ /// - pods: List of VersionedPods to install
77
+ /// - directory: Destination directory for the pods.
78
+ /// - customSpecRepos: Additional spec repos to check for installation.
79
+ /// - Returns: A dictionary of PodInfo's keyed by the pod name.
106
80
@discardableResult
107
81
public static func installPods( _ pods: [ VersionedPod ] ,
108
82
inDir directory: URL ,
109
- customSpecRepos: [ URL ] ? = nil ) -> [ PodInfo ] {
83
+ customSpecRepos: [ URL ] ? = nil ) -> [ String : PodInfo ] {
110
84
let fileManager = FileManager . default
111
85
// Ensure the directory exists, otherwise we can't install all subspecs.
112
86
guard fileManager. directoryExists ( at: directory) else {
@@ -148,31 +122,65 @@ public enum CocoaPodUtils {
148
122
}
149
123
}
150
124
151
- /// Load versions of installed Pods from the contents of a `Podfile.lock` file.
125
+ /// Load installed Pods from the contents of a `Podfile.lock` file.
152
126
///
153
127
/// - Parameter contents: The contents of a `Podfile.lock` file.
154
- /// - Returns: A dictionary with names of the pod for keys and a string representation of the
155
- /// version for values.
156
- public static func loadVersionsFromPodfileLock( contents: String ) -> [ String : String ] {
157
- // This pattern matches a framework name with its version (two to three components)
128
+ /// - Returns: A dictionary of PodInfo structs keyed by the pod name.
129
+ public static func loadPodInfoFromPodfileLock( contents: String ) -> [ String : PodInfo ] {
130
+ // This pattern matches a pod name with its version (two to three components)
158
131
// Examples:
159
132
// - FirebaseUI/Google (4.1.1):
160
133
// - GoogleSignIn (4.0.2):
161
134
162
135
// Force unwrap the regular expression since we know it will work, it's a constant being passed
163
136
// in. If any changes are made, be sure to run this script to ensure it works.
164
- let regex = try ! NSRegularExpression ( pattern: " - (.+) \\ (( \\ d+ \\ . \\ d+ \\ .? \\ d*) \\ ) " ,
165
- options: [ ] )
137
+ let podRegex = try ! NSRegularExpression ( pattern: " - (.+) \\ (( \\ d+ \\ . \\ d+ \\ .? \\ d*) \\ ) " ,
138
+ options: [ ] )
139
+ let depRegex : NSRegularExpression = try ! NSRegularExpression ( pattern: " - (.+).* " ,
140
+ options: [ ] )
166
141
let quotes = CharacterSet ( charactersIn: " \" " )
167
- var frameworks : [ String : String ] = [ : ]
168
- contents. components ( separatedBy: . newlines) . forEach { line in
169
- if let ( framework, version) = detectVersion ( fromLine: line, matching: regex) {
170
- let coreFramework = framework. components ( separatedBy: " / " ) [ 0 ]
171
- let key = coreFramework. trimmingCharacters ( in: quotes)
172
- frameworks [ key] = version
142
+ var pods : [ String : String ] = [ : ]
143
+ var deps : [ String : Set < String > ] = [ : ]
144
+ var currentPod : String ?
145
+ for line in contents. components ( separatedBy: . newlines) {
146
+ if line. starts ( with: " DEPENDENCIES: " ) {
147
+ break
148
+ }
149
+ if let ( pod, version) = detectVersion ( fromLine: line, matching: podRegex) {
150
+ let corePod = pod. components ( separatedBy: " / " ) [ 0 ]
151
+ currentPod = corePod. trimmingCharacters ( in: quotes)
152
+ pods [ currentPod!] = version
153
+ } else if let currentPod = currentPod {
154
+ let matches = depRegex. matches ( in: line, range: NSRange ( location: 0 , length: line. utf8. count) )
155
+ // Match something like - GTMSessionFetcher/Full (= 1.3.0)
156
+ if let match = matches. first {
157
+ let depLine = ( line as NSString ) . substring ( with: match. range ( at: 0 ) ) as String
158
+ // Split spaces and subspecs.
159
+ let dep = depLine. components ( separatedBy: [ " " , " / " ] ) [ 2 ] . trimmingCharacters ( in: quotes)
160
+ if dep != currentPod {
161
+ if deps [ currentPod] == nil {
162
+ deps [ currentPod] = Set ( )
163
+ }
164
+ deps [ currentPod] ? . insert ( dep)
165
+ }
166
+ }
173
167
}
174
168
}
175
- return frameworks
169
+
170
+ // Generate an InstalledPod for each Pod found.
171
+ let podsDir = projectDir. appendingPathComponent ( " Pods " )
172
+ var installedPods : [ String : PodInfo ] = [ : ]
173
+ for (podName, version) in pods {
174
+ let podDir = podsDir. appendingPathComponent ( podName)
175
+ guard FileManager . default. directoryExists ( at: podDir) else {
176
+ fatalError ( " Directory for \( podName) doesn't exist at \( podDir) - failed while getting " +
177
+ " information for installed Pods. " )
178
+ }
179
+ let dependencies = [ String] ( deps [ podName] ?? [ ] )
180
+ let podInfo = PodInfo ( version: version, dependencies: dependencies, installedLocation: podDir)
181
+ installedPods [ podName] = podInfo
182
+ }
183
+ return installedPods
176
184
}
177
185
178
186
public static func updateRepos( ) {
@@ -212,6 +220,33 @@ public enum CocoaPodUtils {
212
220
}
213
221
}
214
222
223
+ /// Get all transitive pod dependencies for a pod.
224
+ /// - Returns: An array of Strings of pod names.
225
+ static func transitivePodDependencies( for podName: String ,
226
+ in installedPods: [ String : PodInfo ] ) -> [ String ] {
227
+ var newDeps = Set ( [ podName] )
228
+ var returnDeps = Set < String > ( )
229
+ repeat {
230
+ var foundDeps = Set < String > ( )
231
+ for dep in newDeps {
232
+ let childDeps = installedPods [ dep] ? . dependencies ?? [ ]
233
+ foundDeps. formUnion ( Set ( childDeps) )
234
+ }
235
+ newDeps = foundDeps. subtracting ( returnDeps)
236
+ returnDeps. formUnion ( newDeps)
237
+ } while newDeps. count > 0
238
+ return Array ( returnDeps)
239
+ }
240
+
241
+ /// Get all transitive pod dependencies for a pod.
242
+ /// - Returns: An array of dependencies with versions for a given pod.
243
+ static func transitiveVersionedPodDependencies( for podName: String ,
244
+ in installedPods: [ String : PodInfo ] ) -> [ VersionedPod ] {
245
+ return transitivePodDependencies ( for: podName, in: installedPods) . map {
246
+ CocoaPodUtils . VersionedPod ( name: $0, version: installedPods [ $0] ? . version)
247
+ }
248
+ }
249
+
215
250
// MARK: - Private Helpers
216
251
217
252
// Tests the input to see if it matches a CocoaPod framework and its version.
@@ -279,44 +314,6 @@ public enum CocoaPodUtils {
279
314
return podfile
280
315
}
281
316
282
- /// Parse the output from Pods Cache
283
- private static func parsePodsCache( output: [ String ] ) -> [ String : [ String : PodInfo ] ] {
284
- var podName : String ?
285
- var podVersion : String ?
286
-
287
- var podsCache : [ String : [ String : PodInfo ] ] = [ : ]
288
-
289
- for line in output {
290
- let trimmedLine = line. trimmingCharacters ( in: . whitespaces)
291
- let parts = trimmedLine. components ( separatedBy: " : " )
292
- if trimmedLine. hasSuffix ( " : " ) {
293
- podName = parts [ 0 ]
294
- } else {
295
- guard parts. count == 2 else { continue }
296
- let key = parts [ 0 ] . trimmingCharacters ( in: . whitespaces)
297
- let value = parts [ 1 ] . trimmingCharacters ( in: . whitespaces)
298
-
299
- switch key {
300
- case " - Version " :
301
- podVersion = value
302
- case " Pod " :
303
- let podLocation = URL ( fileURLWithPath: value)
304
- let podInfo = PodInfo ( versionedPod: VersionedPod ( name: podName!, version: podVersion!) ,
305
- installedLocation: podLocation)
306
- if podsCache [ podName!] == nil {
307
- podsCache [ podName!] = [ : ]
308
- }
309
- podsCache [ podName!] ![ podVersion!] = podInfo
310
-
311
- default :
312
- break
313
- }
314
- }
315
- }
316
-
317
- return podsCache
318
- }
319
-
320
317
/// Write a podfile that contains all the pods passed in to the directory passed in with a name
321
318
/// "Podfile".
322
319
private static func writePodfile( for pods: [ VersionedPod ] ,
0 commit comments