mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
e7f753785e
* Fix recursive include path when relativePath is not set If relativePath is not set on a particular include, the first level of include will currently work, but starting at the second level of iteration, the computed include path will fail as relativePath will be appended over and over onto the filePath. We're fixing that recursion problem here and adding the corresponding tests to make sure it doesn't happen again. * Include projectRoot in include paths The projectRoot setting (when specified) is currently ignored when computing the include paths. We're fixing that in that commit. * Use memoization during recursive SpecFiles creation SpecFile objects are created by recursive through includes. On a large project with programatically generated SpecFile, it is not rare to have hundreds of SpecFiles, creating a large web of include dependencies. In such a case, it is not rare either for a particular SpecFile to be included by multiple other SpecFiles. When that happens, XcodeGen currently creates a SpecFile object every time a SpecFile gets included, which can lead to an exponential growth of includes. I have seen hundreds of files being turned into hundred of thousands of SpecFile object creations, which leads to an impractical XcodeGen run of tens of minutes. This change adds memoization during SpecFile recursion, in order to reuse the previously created SpecFiles, if available, instead of re-creating them. * Update CHANGELOG.md Add the following changes to the changelog: * b97bdc4 - Use memoization during recursive SpecFiles creation * a6b96ad - Include projectRoot in include paths * 557b074 - Fix recursive include path when relativePath is not set
74 lines
2.2 KiB
Swift
74 lines
2.2 KiB
Swift
import Foundation
|
|
import JSONUtilities
|
|
import PathKit
|
|
import XcodeProj
|
|
import Yams
|
|
import Version
|
|
|
|
public class SpecLoader {
|
|
|
|
var project: Project!
|
|
public private(set) var projectDictionary: [String: Any]?
|
|
let version: Version
|
|
|
|
public init(version: Version) {
|
|
self.version = version
|
|
}
|
|
|
|
public func loadProject(path: Path, projectRoot: Path? = nil, variables: [String: String] = [:]) throws -> Project {
|
|
var cachedSpecFiles: [Path: SpecFile] = [:]
|
|
let spec = try SpecFile(filePath: path, basePath: projectRoot ?? path.parent(), cachedSpecFiles: &cachedSpecFiles, variables: variables)
|
|
let resolvedDictionary = spec.resolvedDictionary()
|
|
let project = try Project(basePath: projectRoot ?? spec.basePath, jsonDictionary: resolvedDictionary)
|
|
|
|
self.project = project
|
|
projectDictionary = resolvedDictionary
|
|
|
|
return project
|
|
}
|
|
|
|
public func validateProjectDictionaryWarnings() throws {
|
|
try projectDictionary?.validateWarnings()
|
|
}
|
|
|
|
public func generateCacheFile() throws -> CacheFile? {
|
|
guard let projectDictionary = projectDictionary,
|
|
let project = project else {
|
|
return nil
|
|
}
|
|
return try CacheFile(
|
|
version: version,
|
|
projectDictionary: projectDictionary,
|
|
project: project
|
|
)
|
|
}
|
|
}
|
|
|
|
private extension Dictionary where Key == String, Value: Any {
|
|
|
|
func validateWarnings() throws {
|
|
let errors: [SpecValidationError.ValidationError] = []
|
|
|
|
if !errors.isEmpty {
|
|
throw SpecValidationError(errors: errors)
|
|
}
|
|
}
|
|
|
|
func hasValueContaining(_ needle: String) -> Bool {
|
|
values.contains { value in
|
|
switch value {
|
|
case let dictionary as JSONDictionary:
|
|
return dictionary.hasValueContaining(needle)
|
|
case let string as String:
|
|
return string.contains(needle)
|
|
case let array as [JSONDictionary]:
|
|
return array.contains { $0.hasValueContaining(needle) }
|
|
case let array as [String]:
|
|
return array.contains { $0.contains(needle) }
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|