Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix includes related issues and improve their performances #1275

Merged
merged 4 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
- Added support for `enableGPUFrameCaptureMode` #1251 @bsudekum
- Config setting presets can now also be loaded from the main bundle when bundling XcodeGenKit #1135 @SofteqDG
- Added ability to generate multiple projects in one XcodeGen launch #1270 @skofgar
- Use memoization during recursive SpecFiles creation. This provides a drastic performance boost with lots of recursive includes #1275 @ma-oli

### Fixed

- Fix scheme not being generated for aggregate targets #1250 @CraigSiemens
- Fix recursive include path when relativePath is not set #1275 @ma-oli
- Include projectRoot in include paths #1275 @ma-oli

## 2.32.0

Expand Down
9 changes: 8 additions & 1 deletion Sources/ProjectSpec/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,14 @@ extension Project: Equatable {
extension Project {

public init(path: Path) throws {
let spec = try SpecFile(path: path)
var cachedSpecFiles: [Path: SpecFile] = [:]
let spec = try SpecFile(filePath: path, basePath: path.parent(), cachedSpecFiles: &cachedSpecFiles)
try self.init(spec: spec)
}

public init(path: Path, basePath: Path) throws {
var cachedSpecFiles: [Path: SpecFile] = [:]
let spec = try SpecFile(filePath: path, basePath: basePath, cachedSpecFiles: &cachedSpecFiles)
try self.init(spec: spec)
}

Expand Down
39 changes: 25 additions & 14 deletions Sources/ProjectSpec/SpecFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public struct SpecFile {
}
}

public init(path: Path, variables: [String: String] = [:]) throws {
try self.init(filePath: path, basePath: path.parent(), variables: variables)
public init(path: Path, cachedSpecFiles: inout [Path: SpecFile], variables: [String: String] = [:]) throws {
try self.init(filePath: path, basePath: path.parent(), cachedSpecFiles: &cachedSpecFiles, variables: variables)
}

public init(filePath: Path, jsonDictionary: JSONDictionary, basePath: Path = "", relativePath: Path = "", subSpecs: [SpecFile] = []) {
Expand All @@ -60,25 +60,29 @@ public struct SpecFile {
self.filePath = filePath
}

private init(include: Include, basePath: Path, relativePath: Path, variables: [String: String]) throws {
let basePath = include.relativePaths ? (basePath + relativePath) : (basePath + relativePath + include.path.parent())
private init(include: Include, basePath: Path, relativePath: Path, cachedSpecFiles: inout [Path: SpecFile], variables: [String: String]) throws {
let basePath = include.relativePaths ? (basePath + relativePath) : basePath
let relativePath = include.relativePaths ? include.path.parent() : Path()
let includePath = include.relativePaths ? basePath + relativePath + include.path.lastComponent : basePath + include.path

try self.init(filePath: include.path, basePath: basePath, variables: variables, relativePath: relativePath)
try self.init(filePath: includePath, basePath: basePath, cachedSpecFiles: &cachedSpecFiles, variables: variables, relativePath: relativePath)
}

private init(filePath: Path, basePath: Path, variables: [String: String], relativePath: Path = "") throws {
let path = basePath + relativePath + filePath.lastComponent
let jsonDictionary = try SpecFile.loadDictionary(path: path).expand(variables: variables)

public init(filePath: Path, basePath: Path, cachedSpecFiles: inout [Path: SpecFile], variables: [String: String] = [:], relativePath: Path = "") throws {
let jsonDictionary = try SpecFile.loadDictionary(path: filePath).expand(variables: variables)
let includes = Include.parse(json: jsonDictionary["include"])
let subSpecs: [SpecFile] = try includes
.filter(\.enable)
.map { include in
try SpecFile(include: include, basePath: basePath, relativePath: relativePath, variables: variables)
if let specFile = cachedSpecFiles[filePath] {
return specFile
} else {
return try SpecFile(include: include, basePath: basePath, relativePath: relativePath, cachedSpecFiles: &cachedSpecFiles, variables: variables)
}
}

self.init(filePath: filePath, jsonDictionary: jsonDictionary, basePath: basePath, relativePath: relativePath, subSpecs: subSpecs)
cachedSpecFiles[filePath] = self
}

static func loadDictionary(path: Path) throws -> JSONDictionary {
Expand All @@ -100,7 +104,8 @@ public struct SpecFile {
}

private func resolvedDictionaryWithUniqueTargets() -> JSONDictionary {
let resolvedSpec = resolvingPaths()
var cachedSpecFiles: [Path: SpecFile] = [:]
let resolvedSpec = resolvingPaths(cachedSpecFiles: &cachedSpecFiles)

var value = Set<String>()
return resolvedSpec.mergedDictionary(set: &value)
Expand All @@ -118,19 +123,25 @@ public struct SpecFile {
.reduce([:]) { $1.merged(onto: $0) })
}

func resolvingPaths(relativeTo basePath: Path = Path()) -> SpecFile {
func resolvingPaths(cachedSpecFiles: inout [Path: SpecFile], relativeTo basePath: Path = Path()) -> SpecFile {
if let cachedSpecFile = cachedSpecFiles[filePath] {
return cachedSpecFile
}

let relativePath = (basePath + self.relativePath).normalize()
guard relativePath != Path() else {
return self
}

let jsonDictionary = Project.pathProperties.resolvingPaths(in: self.jsonDictionary, relativeTo: relativePath)
return SpecFile(
let specFile = SpecFile(
filePath: filePath,
jsonDictionary: jsonDictionary,
relativePath: self.relativePath,
subSpecs: subSpecs.map { $0.resolvingPaths(relativeTo: relativePath) }
subSpecs: subSpecs.map { $0.resolvingPaths(cachedSpecFiles: &cachedSpecFiles, relativeTo: relativePath) }
)
cachedSpecFiles[filePath] = specFile
return specFile
}
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/ProjectSpec/SpecLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public class SpecLoader {
}

public func loadProject(path: Path, projectRoot: Path? = nil, variables: [String: String] = [:]) throws -> Project {
let spec = try SpecFile(path: path, variables: variables)
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)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
configFiles:
IncludedConfig: config
include:
- path: legacy_paths_test/recursive_include.yml
relativePaths: false
options:
carthageBuildPath: carthage_build
carthageExecutablePath: carthage_executable
Expand All @@ -9,8 +12,6 @@ targets:
platform: tvOS
configFiles:
Config: config
sources:
- source
dependencies:
- framework: Framework
info:
Expand Down
4 changes: 4 additions & 0 deletions Tests/Fixtures/legacy_paths_test/recursive_include.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
targets:
IncludedTarget:
sources:
- source
5 changes: 4 additions & 1 deletion Tests/PerformanceTests/PerformanceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class GeneratedPerformanceTests: XCTestCase {
let project = try Project.testProject(basePath: basePath)
let specPath = basePath + "project.yaml"
try dumpYamlDictionary(project.toJSONDictionary(), path: specPath)
var cachedSpecFiles: [Path: SpecFile] = [:]

measure {
let spec = try! SpecFile(path: specPath, variables: ProcessInfo.processInfo.environment)
let spec = try! SpecFile(path: specPath,
cachedSpecFiles: &cachedSpecFiles,
variables: ProcessInfo.processInfo.environment)
_ = spec.resolvedDictionary()
}
}
Expand Down