mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2024-12-11 07:16:40 +03:00
Optimize bottlenecks (#803)
This commit is contained in:
parent
01c6959f48
commit
c3693d4098
@ -1,6 +1,8 @@
|
||||
# Change Log
|
||||
|
||||
## Next Version
|
||||
#### Added
|
||||
- Improve speed of metadata parsing and dependency resolution. [#803](https://github.com/yonaskolb/XcodeGen/pull/803) @michaeleisel
|
||||
|
||||
## 2.15.1
|
||||
|
||||
|
@ -4,18 +4,51 @@ import PathKit
|
||||
import Yams
|
||||
|
||||
extension Dictionary where Key: JSONKey {
|
||||
|
||||
public func json<T: NamedJSONDictionaryConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove) throws -> [T] {
|
||||
public func json<T: NamedJSONDictionaryConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove, parallel: Bool = false) throws -> [T] {
|
||||
guard let dictionary = json(atKeyPath: keyPath) as JSONDictionary? else {
|
||||
return []
|
||||
}
|
||||
var items: [T] = []
|
||||
for (key, _) in dictionary {
|
||||
let jsonDictionary: JSONDictionary = try dictionary.json(atKeyPath: .key(key))
|
||||
let item = try T(name: key, jsonDictionary: jsonDictionary)
|
||||
items.append(item)
|
||||
if parallel {
|
||||
let defaultError = NSError(domain: "Unspecified error", code: 0, userInfo: nil)
|
||||
var itemResults: [Result<T, Error>] = Array(repeating: .failure(defaultError), count: dictionary.count)
|
||||
var ops: [BlockOperation] = []
|
||||
var idx: Int = 0
|
||||
for (key, _) in dictionary {
|
||||
ops.append(BlockOperation { [idx] in
|
||||
do {
|
||||
let jsonDictionary: JSONDictionary = try dictionary.json(atKeyPath: .key(key))
|
||||
let item = try T(name: key, jsonDictionary: jsonDictionary)
|
||||
itemResults[idx] = .success(item)
|
||||
} catch {
|
||||
itemResults[idx] = .failure(error)
|
||||
}
|
||||
})
|
||||
idx += 1
|
||||
}
|
||||
let queue = OperationQueue()
|
||||
queue.qualityOfService = .userInteractive
|
||||
queue.maxConcurrentOperationCount = 8
|
||||
queue.addOperations(ops, waitUntilFinished: true)
|
||||
var items = ContiguousArray<T>()
|
||||
items.reserveCapacity(itemResults.count)
|
||||
for result in itemResults {
|
||||
switch result {
|
||||
case .failure(let error):
|
||||
throw error
|
||||
case .success(let item):
|
||||
items.append(item)
|
||||
}
|
||||
}
|
||||
return Array(items)
|
||||
} else {
|
||||
var items: [T] = []
|
||||
for (key, _) in dictionary {
|
||||
let jsonDictionary: JSONDictionary = try dictionary.json(atKeyPath: .key(key))
|
||||
let item = try T(name: key, jsonDictionary: jsonDictionary)
|
||||
items.append(item)
|
||||
}
|
||||
return items
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
public func json<T: NamedJSONConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove) throws -> [T] {
|
||||
|
@ -171,7 +171,7 @@ extension Project {
|
||||
let configs: [String: String] = jsonDictionary.json(atKeyPath: "configs") ?? [:]
|
||||
self.configs = configs.isEmpty ? Config.defaultConfigs :
|
||||
configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
|
||||
targets = try jsonDictionary.json(atKeyPath: "targets").sorted { $0.name < $1.name }
|
||||
targets = try jsonDictionary.json(atKeyPath: "targets", parallel: true).sorted { $0.name < $1.name }
|
||||
aggregateTargets = try jsonDictionary.json(atKeyPath: "aggregateTargets").sorted { $0.name < $1.name }
|
||||
projectReferences = try jsonDictionary.json(atKeyPath: "projectReferences").sorted { $0.name < $1.name }
|
||||
schemes = try jsonDictionary.json(atKeyPath: "schemes")
|
||||
|
@ -10,11 +10,14 @@ import ProjectSpec
|
||||
import PathKit
|
||||
|
||||
public class CarthageDependencyResolver {
|
||||
static func getBuildPath(_ project: Project) -> String {
|
||||
return project.options.carthageBuildPath ?? "Carthage/Build"
|
||||
}
|
||||
|
||||
/// Carthage's base build path as specified by the
|
||||
/// project's `SpecOptions`, or `Carthage/Build` by default
|
||||
var buildPath: String {
|
||||
project.options.carthageBuildPath ?? "Carthage/Build"
|
||||
return CarthageDependencyResolver.getBuildPath(project)
|
||||
}
|
||||
|
||||
/// Carthage's executable path as specified by the
|
||||
@ -24,10 +27,11 @@ public class CarthageDependencyResolver {
|
||||
}
|
||||
|
||||
private let project: Project
|
||||
lazy var versionLoader = CarthageVersionLoader(buildPath: project.basePath + buildPath)
|
||||
let versionLoader: CarthageVersionLoader
|
||||
|
||||
init(project: Project) {
|
||||
self.project = project
|
||||
versionLoader = CarthageVersionLoader(buildPath: project.basePath + CarthageDependencyResolver.getBuildPath(project))
|
||||
}
|
||||
|
||||
/// Carthage's build path for the given platform
|
||||
|
@ -9,24 +9,48 @@ import Foundation
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
|
||||
class Mutex<T> {
|
||||
var value: T
|
||||
var semaphore: DispatchSemaphore = DispatchSemaphore(value: 1)
|
||||
|
||||
init(_ value: T) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
func get<U>(closure: (inout T) throws -> (U)) rethrows -> U {
|
||||
semaphore.wait()
|
||||
defer { semaphore.signal() }
|
||||
return try closure(&value)
|
||||
}
|
||||
|
||||
func get(closure: (inout T) -> ()) {
|
||||
semaphore.wait()
|
||||
closure(&value)
|
||||
semaphore.signal()
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this class can be accessed on multiple threads. It must therefore stay thread-safe.
|
||||
class CarthageVersionLoader {
|
||||
|
||||
private let buildPath: Path
|
||||
private var cachedFiles: [String: CarthageVersionFile] = [:]
|
||||
private var cachedFilesMutex: Mutex<[String: CarthageVersionFile]> = Mutex([:])
|
||||
|
||||
init(buildPath: Path) {
|
||||
self.buildPath = buildPath
|
||||
}
|
||||
|
||||
func getVersionFile(for dependency: String) throws -> CarthageVersionFile {
|
||||
if let versionFile = cachedFiles[dependency] {
|
||||
return versionFile
|
||||
return try cachedFilesMutex.get { cachedFiles in
|
||||
if let versionFile = cachedFiles[dependency] {
|
||||
return versionFile
|
||||
}
|
||||
let filePath = buildPath + ".\(dependency).version"
|
||||
let data = try filePath.read()
|
||||
let carthageVersionFile = try JSONDecoder().decode(CarthageVersionFile.self, from: data)
|
||||
cachedFiles[dependency] = carthageVersionFile
|
||||
return carthageVersionFile
|
||||
}
|
||||
let filePath = buildPath + ".\(dependency).version"
|
||||
let data = try filePath.read()
|
||||
let carthageVersionFile = try JSONDecoder().decode(CarthageVersionFile.self, from: data)
|
||||
cachedFiles[dependency] = carthageVersionFile
|
||||
return carthageVersionFile
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user