Files
XcodeGen/Sources/ProjectSpec/Template.swift
T
Romuald CariandBrentley Jones 06a6616b88 Improving variable expansion runtime (#704)
* Improving variable expansion runtime

The current implementation of variable expansion is O(n x m) with n being the
number of strings in the project spec and m being the number of project variables.

This implementation is now O(n).

Also, this effectively deprecates the support for $legacy_variables in favor of the
${new_variables} making this whole patch possible.

* Adding option to disable variable expansion

* Adding performance test for spec loading

* Updating changelog
2019-11-06 08:33:54 -06:00

77 lines
3.3 KiB
Swift

import Foundation
import JSONUtilities
struct TemplateStructure {
let baseKey: String
let templatesKey: String
let nameToReplace: String
}
extension Target {
static func resolveTargetTemplates(jsonDictionary: JSONDictionary) -> JSONDictionary {
return resolveTemplates(jsonDictionary: jsonDictionary,
templateStructure: TemplateStructure(baseKey: "targets",
templatesKey: "targetTemplates",
nameToReplace: "target_name"))
}
}
extension Scheme {
static func resolveSchemeTemplates(jsonDictionary: JSONDictionary) -> JSONDictionary {
return resolveTemplates(jsonDictionary: jsonDictionary,
templateStructure: TemplateStructure(baseKey: "schemes",
templatesKey: "schemeTemplates",
nameToReplace: "scheme_name"))
}
}
private func resolveTemplates(jsonDictionary: JSONDictionary, templateStructure: TemplateStructure) -> JSONDictionary {
guard var baseDictionary: [String: JSONDictionary] = jsonDictionary[templateStructure.baseKey] as? [String: JSONDictionary] else {
return jsonDictionary
}
let templatesDictionary: [String: JSONDictionary] = jsonDictionary[templateStructure.templatesKey] as? [String: JSONDictionary] ?? [:]
// Recursively collects all nested template names of a given dictionary.
func collectTemplates(of jsonDictionary: JSONDictionary,
into allTemplates: inout [String],
insertAt insertionIndex: inout Int) {
guard let templates = jsonDictionary["templates"] as? [String] else {
return
}
for template in templates where !allTemplates.contains(template) {
guard let templateDictionary = templatesDictionary[template] else {
continue
}
allTemplates.insert(template, at: insertionIndex)
collectTemplates(of: templateDictionary, into: &allTemplates, insertAt: &insertionIndex)
insertionIndex += 1
}
}
for (referenceName, var reference) in baseDictionary {
var templates: [String] = []
var index: Int = 0
collectTemplates(of: reference, into: &templates, insertAt: &index)
if !templates.isEmpty {
var mergedDictionary: JSONDictionary = [:]
for template in templates {
if let templateDictionary = templatesDictionary[template] {
mergedDictionary = templateDictionary.merged(onto: mergedDictionary)
}
}
reference = reference.merged(onto: mergedDictionary)
reference = reference.expand(variables: [templateStructure.nameToReplace: referenceName])
if let templateAttributes = reference["templateAttributes"] as? [String: String] {
reference = reference.expand(variables: templateAttributes)
}
}
baseDictionary[referenceName] = reference
}
var jsonDictionary = jsonDictionary
jsonDictionary[templateStructure.baseKey] = baseDictionary
return jsonDictionary
}