From 05fa244a899e4c6e76d77d92d0d8ca660ebc2e82 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Thu, 10 Jan 2019 11:42:57 +0000 Subject: [PATCH 01/29] Add $(CURDIR) helper to expand the base path of the spec file --- Sources/ProjectSpec/SpecLoader.swift | 36 +++++++++++++++++++ Tests/Fixtures/paths_test.yml | 8 +++++ .../paths_test/included_paths_test.yml | 6 ++++ Tests/XcodeGenKitTests/SpecLoadingTests.swift | 8 +++++ 4 files changed, 58 insertions(+) create mode 100644 Tests/Fixtures/paths_test.yml create mode 100644 Tests/Fixtures/paths_test/included_paths_test.yml diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 0542081a..8c5c0507 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -43,6 +43,42 @@ extension Project { } json = merge(dictionary: json, onto: includeDictionary) } + + let basePath = path.parent() + json = expandPaths(in: json, basePath: basePath) + return json } + + private static func expandPaths(in source: Dictionary, basePath: Path) -> Dictionary { + var result = source + + for (key, value) in source { + if let value = value as? String { + result[key] = value.replacingOccurrences(of: "$(CURDIR)", with: basePath.string) + } else if let value = value as? Array { + result[key] = expandPaths(in: value, basePath: basePath) + } else if let value = value as? Dictionary { + result[key] = expandPaths(in: value, basePath: basePath) + } + } + + return result + } + + private static func expandPaths(in source: Array, basePath: Path) -> Array { + var result = source + + for (index, value) in source.enumerated() { + if let value = value as? String { + result[index] = value.replacingOccurrences(of: "$(CURDIR)", with: basePath.string) + } else if let value = value as? Array { + result[index] = expandPaths(in: value, basePath: basePath) + } else if let value = value as? Dictionary { + result[index] = expandPaths(in: value, basePath: basePath) + } + } + + return result + } } diff --git a/Tests/Fixtures/paths_test.yml b/Tests/Fixtures/paths_test.yml new file mode 100644 index 00000000..3f209401 --- /dev/null +++ b/Tests/Fixtures/paths_test.yml @@ -0,0 +1,8 @@ +include: paths_test/included_paths_test.yml +name: NewName +targets: + NewTarget: + type: application + platform: iOS + sources: + - $(CURDIR)/source diff --git a/Tests/Fixtures/paths_test/included_paths_test.yml b/Tests/Fixtures/paths_test/included_paths_test.yml new file mode 100644 index 00000000..f04bece6 --- /dev/null +++ b/Tests/Fixtures/paths_test/included_paths_test.yml @@ -0,0 +1,6 @@ +targets: + IncludedTarget: + type: application + platform: iOS + sources: + - $(CURDIR)/source diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 565599e7..7a13de6b 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -10,6 +10,14 @@ class SpecLoadingTests: XCTestCase { func testSpecLoader() { describe { + $0.it("expands directories") { + let path = fixturePath + "paths_test.yml" + let project = try Project(path: path) + + try expect(project.targets.first!.sources.first!.path) == (fixturePath + "paths_test" + "source").string + try expect(project.targets.last!.sources.first!.path) == (fixturePath + "source").string + } + $0.it("merges includes") { let path = fixturePath + "include_test.yml" let project = try Project(path: path) From a9ecef37bcb1963f284ba5d5b6590d1f17c514c1 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:06:05 +0000 Subject: [PATCH 02/29] Remove the $(CURDIR) expansion (it was a bad idea) --- Sources/ProjectSpec/SpecLoader.swift | 35 +--------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 8c5c0507..e5240c08 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -4,7 +4,7 @@ import PathKit import Yams extension Project { - + public init(path: Path) throws { let dictionary = try Project.loadDictionary(path: path) try self.init(basePath: path.parent(), jsonDictionary: dictionary) @@ -44,41 +44,8 @@ extension Project { json = merge(dictionary: json, onto: includeDictionary) } - let basePath = path.parent() - json = expandPaths(in: json, basePath: basePath) - return json } - private static func expandPaths(in source: Dictionary, basePath: Path) -> Dictionary { - var result = source - - for (key, value) in source { - if let value = value as? String { - result[key] = value.replacingOccurrences(of: "$(CURDIR)", with: basePath.string) - } else if let value = value as? Array { - result[key] = expandPaths(in: value, basePath: basePath) - } else if let value = value as? Dictionary { - result[key] = expandPaths(in: value, basePath: basePath) - } - } - - return result - } - - private static func expandPaths(in source: Array, basePath: Path) -> Array { - var result = source - - for (index, value) in source.enumerated() { - if let value = value as? String { - result[index] = value.replacingOccurrences(of: "$(CURDIR)", with: basePath.string) - } else if let value = value as? Array { - result[index] = expandPaths(in: value, basePath: basePath) - } else if let value = value as? Dictionary { - result[index] = expandPaths(in: value, basePath: basePath) - } - } - - return result } } From c90e045bc8e6f2abcb243067fb1028ff4afd58fc Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:08:35 +0000 Subject: [PATCH 03/29] Add a project spec struct to abstract spec files on disk --- Sources/ProjectSpec/ProjectSpec.swift | 90 +++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Sources/ProjectSpec/ProjectSpec.swift diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift new file mode 100644 index 00000000..b0566fec --- /dev/null +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -0,0 +1,90 @@ +import Foundation +import typealias JSONUtilities.JSONDictionary +import PathKit + +extension Project { + + public struct Spec { + public let relativePath: Path + public let jsonDictionary: JSONDictionary + public let subSpecs: [Spec] + + public init(relativePath: Path, jsonDictionary: JSONDictionary, subSpecs: [Spec] = []) { + self.relativePath = relativePath + self.jsonDictionary = jsonDictionary + self.subSpecs = subSpecs + } + + public init(filename: String, basePath: Path, relativePath: Path = "") throws { + let path = basePath + relativePath + filename + + // Depending on the extension we will either load the file as YAML or JSON + var json: [String: Any] + if path.extension?.lowercased() == "json" { + let data: Data = try path.read() + let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) + guard let jsonDictionary = jsonData as? [String: Any] else { + fatalError("Invalid JSON at path \(path)") + } + json = jsonDictionary + } else { + json = try loadYamlDictionary(path: path) + } + + var includeStrings: [String] + if let includeString = json["include"] as? String { + includeStrings = [includeString] + } else if let includeArray = json["include"] as? [String] { + includeStrings = includeArray + } else { + includeStrings = [] + } + + let includes = try includeStrings.map { include -> Spec in + let path = Path(include) + return try Spec(filename: path.lastComponent, basePath: basePath + relativePath, relativePath: path.parent()) + } + + self.relativePath = relativePath + self.jsonDictionary = json + self.subSpecs = includes + } + + public func resolvedDictionary() -> JSONDictionary { + return jsonDictionary.merged(onto: + subSpecs + .map { $0.resolvedDictionary() } + .reduce([:]) { $1.merged(onto: $0) } + ) + } + } +} + +internal protocol SubSequenceConvertible: Sequence { + + init(_ subsequence: SubSequence) +} + +extension String: SubSequenceConvertible { } +extension Substring: SubSequenceConvertible { } + +extension Dictionary where Key: StringProtocol, Key: SubSequenceConvertible, Value: Any { + + func merged(onto other: Dictionary) -> Dictionary { + var merged = other + + for (key, value) in self { + if key.hasSuffix(":REPLACE") { + let newKey = key[key.startIndex ..< key.index(key.endIndex, offsetBy: -8)] + merged[Key(newKey)] = value + } else if let dictionary = value as? Dictionary, let base = merged[key] as? Dictionary { + merged[key] = dictionary.merged(onto: base) as? Value + } else if let array = value as? [Any], let base = merged[key] as? [Any] { + merged[key] = (base + array) as? Value + } else { + merged[key] = value + } + } + return merged + } +} From 8928088dfe708307fa67ea3e4cbc010d2fad905f Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:14:52 +0000 Subject: [PATCH 04/29] Use the Spec struct rather than dictionaries directly --- Sources/ProjectSpec/Project.swift | 6 ++-- Sources/ProjectSpec/SpecLoader.swift | 44 ++-------------------------- Sources/XcodeGenKit/SpecLoader.swift | 6 ++-- 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 808b964c..c5f78e52 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -132,9 +132,11 @@ extension Project: Equatable { extension Project { - public init(basePath: Path, jsonDictionary: JSONDictionary) throws { + public init(spec: Spec, basePath: Path) throws { self.basePath = basePath - let jsonDictionary = try Project.resolveProject(jsonDictionary: jsonDictionary) + + let jsonDictionary = try Project.resolveProject(jsonDictionary: spec.resolvedDictionary()) + name = try jsonDictionary.json(atKeyPath: "name") settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty settingGroups = jsonDictionary.json(atKeyPath: "settingGroups") diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index e5240c08..72108633 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -6,46 +6,8 @@ import Yams extension Project { public init(path: Path) throws { - let dictionary = try Project.loadDictionary(path: path) - try self.init(basePath: path.parent(), jsonDictionary: dictionary) - } - - public static func loadDictionary(path: Path) throws -> JSONDictionary { - - // Depending on the extension we will either load the file as YAML or JSON - var json: [String: Any] - if path.extension?.lowercased() == "json" { - let data: Data = try path.read() - let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) - guard let jsonDictionary = jsonData as? [String: Any] else { - fatalError("Invalid JSON at path \(path)") - } - json = jsonDictionary - } else { - json = try loadYamlDictionary(path: path) - } - - var includes: [String] - if let includeString = json["include"] as? String { - includes = [includeString] - } else if let includeArray = json["include"] as? [String] { - includes = includeArray - } else { - includes = [] - } - - if !includes.isEmpty { - var includeDictionary: JSONDictionary = [:] - for include in includes { - let includePath = path.parent() + include - let dictionary = try loadDictionary(path: includePath) - includeDictionary = merge(dictionary: dictionary, onto: includeDictionary) - } - json = merge(dictionary: json, onto: includeDictionary) - } - - return json - } - + let basePath = path.parent() + let template = try Spec(filename: path.lastComponent, basePath: basePath) + try self.init(spec: template, basePath: basePath) } } diff --git a/Sources/XcodeGenKit/SpecLoader.swift b/Sources/XcodeGenKit/SpecLoader.swift index b9a35a2b..2bc8eb77 100644 --- a/Sources/XcodeGenKit/SpecLoader.swift +++ b/Sources/XcodeGenKit/SpecLoader.swift @@ -16,11 +16,11 @@ public class SpecLoader { } public func loadProject(path: Path) throws -> Project { - let dictionary = try Project.loadDictionary(path: path) - let project = try Project(basePath: path.parent(), jsonDictionary: dictionary) + let template = try Project.Spec(filename: path.lastComponent, basePath: path.parent()) + let project = try Project(spec: template, basePath: path.parent()) self.project = project - projectDictionary = dictionary + projectDictionary = template.jsonDictionary return project } From a2684a96a45fad15bcd1cd962ad327fcc2d7211f Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:16:17 +0000 Subject: [PATCH 05/29] Replace global merge function with extension on Dictionary --- Sources/ProjectSpec/Settings.swift | 18 ------------------ Sources/ProjectSpec/Target.swift | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/Sources/ProjectSpec/Settings.swift b/Sources/ProjectSpec/Settings.swift index 26d061bf..95254dd6 100644 --- a/Sources/ProjectSpec/Settings.swift +++ b/Sources/ProjectSpec/Settings.swift @@ -101,24 +101,6 @@ extension Dictionary where Key == String, Value: Any { } } -func merge(dictionary: JSONDictionary, onto base: JSONDictionary) -> JSONDictionary { - var merged = base - - for (key, value) in dictionary { - if key.hasSuffix(":REPLACE") { - let newKey = key.replacingOccurrences(of: ":REPLACE", with: "") - merged[newKey] = value - } else if let dictionary = value as? JSONDictionary, let base = merged[key] as? JSONDictionary { - merged[key] = merge(dictionary: dictionary, onto: base) - } else if let array = value as? [Any], let base = merged[key] as? [Any] { - merged[key] = base + array - } else { - merged[key] = value - } - } - return merged -} - public func += (lhs: inout BuildSettings, rhs: BuildSettings?) { guard let rhs = rhs else { return } lhs.merge(rhs) diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index 10218fad..b7727e66 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -125,10 +125,10 @@ extension Target { var mergedDictionary: JSONDictionary = [:] for template in templates { if let templateDictionary = targetTemplatesDictionary[template] { - mergedDictionary = merge(dictionary: templateDictionary, onto: mergedDictionary) + mergedDictionary = templateDictionary.merged(onto: mergedDictionary) } } - target = merge(dictionary: target, onto: mergedDictionary) + target = target.merged(onto: mergedDictionary) } targetsDictionary[targetName] = target } From f9df768ec0ad3a7ce1e7475072a401aab4304d1c Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:18:48 +0000 Subject: [PATCH 06/29] Add a PathContaining protocol for expanding paths to the source file's directory --- Sources/ProjectSpec/SpecLoader.swift | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 72108633..f5cb839e 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -11,3 +11,47 @@ extension Project { try self.init(spec: template, basePath: basePath) } } + +protocol PathContaining { + + associatedtype JSONSourceType + static func expandPaths(for source: JSONSourceType, relativeTo path: Path) -> JSONSourceType +} + +extension PathContaining { + + static func expandStringPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path) -> JSONDictionary { + var result = source + + if let source = result[key] as? String { + result[key] = (path + source).string + } else if let source = result[key] as? [String] { + result[key] = source.map { (path + $0).string } + } else if let source = result[key] as? [String: String] { + result[key] = source.mapValues { (path + $0).string } + } + return result + } + + static func expandChildPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path, type: T.Type) -> JSONDictionary { + var result = source + + if let source = result[key] as? T.JSONSourceType { + result[key] = T.expandPaths(for: source, relativeTo: path) + } else if let source = result[key] as? [T.JSONSourceType] { + result[key] = source.map { T.expandPaths(for: $0, relativeTo: path) } + } else if let source = result[key] as? [String: T.JSONSourceType] { + result[key] = source.mapValues { T.expandPaths(for: $0, relativeTo: path) } + } + return result + } + + static func expandChildPaths(from source: JSONDictionary, forPotentialKeys keys: [String], relativeTo path: Path, type: T.Type) -> JSONDictionary { + var result = source + + for key in keys { + result = expandChildPaths(from: result, forKey: key, relativeTo: path, type: type) + } + return result + } +} From 98aeef46dca728bc5e780a4f2b5c02b990178cb1 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:22:34 +0000 Subject: [PATCH 07/29] Implement PathContaining on relevant models --- Sources/ProjectSpec/AggregateTarget.swift | 15 ++++++++++++++ Sources/ProjectSpec/BuildScript.swift | 8 ++++++++ Sources/ProjectSpec/Dependency.swift | 8 ++++++++ Sources/ProjectSpec/Plist.swift | 7 +++++++ Sources/ProjectSpec/Project.swift | 24 ++++++++++++++++++++++ Sources/ProjectSpec/SpecOptions.swift | 13 ++++++++++++ Sources/ProjectSpec/Target.swift | 25 +++++++++++++++++++++++ Sources/ProjectSpec/TargetSource.swift | 13 +++++++++++- 8 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index 45fcba14..27c445bc 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path public struct AggregateTarget: ProjectTarget { public var name: String @@ -61,3 +62,17 @@ extension AggregateTarget: NamedJSONDictionaryConvertible { attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:] } } + +extension AggregateTarget: PathContaining { + + static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { + var result = source + + for (targetName, var target) in result { + target = expandStringPaths(from: target, forKey: "configFiles", relativeTo: path) + target = expandChildPaths(from: target, forKey: "buildScripts", relativeTo: path, type: BuildScript.self) + result[targetName] = target + } + return result + } +} diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index 30b63404..6d3315bd 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path public struct BuildScript: Equatable { @@ -53,3 +54,10 @@ extension BuildScript: JSONObjectConvertible { showEnvVars = jsonDictionary.json(atKeyPath: "showEnvVars") ?? true } } + +extension BuildScript: PathContaining { + + static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + return expandStringPaths(from: source, forKey: "path", relativeTo: path) + } +} diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 722aae65..76641d8f 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path public struct Dependency: Equatable { @@ -72,3 +73,10 @@ extension Dependency: JSONObjectConvertible { } } } + +extension Dependency: PathContaining { + + static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + return expandStringPaths(from: source, forKey: "framework", relativeTo: path) + } +} diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index 5b2a502a..d66721a2 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path public struct Plist: Equatable { @@ -24,3 +25,9 @@ extension Plist: JSONObjectConvertible { properties = jsonDictionary.json(atKeyPath: "properties") ?? [:] } } + +extension Plist: PathContaining { + static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + return expandStringPaths(from: source, forKey: "path", relativeTo: path) + } +} diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index c5f78e52..2f948f26 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -168,6 +168,30 @@ extension Project { } } +extension Project: PathContaining { + + static func expandPaths(for spec: Spec, relativeTo basePath: Path = "") -> Spec { + let relativePath = (basePath + spec.relativePath).normalize() + guard relativePath != Path() else { + return spec + } + + var jsonDictionary = spec.jsonDictionary + + jsonDictionary = expandStringPaths(from: jsonDictionary, forKey: "configFiles", relativeTo: relativePath) + jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "options", relativeTo: relativePath, type: SpecOptions.self) + jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "targets", relativeTo: relativePath, type: Target.self) + jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "aggregateTargets", relativeTo: relativePath, type: AggregateTarget.self) + return Spec( + relativePath: spec.relativePath, + jsonDictionary: jsonDictionary, + subSpecs: spec.subSpecs.map { template in + return Project.expandPaths(for: template, relativeTo: relativePath) + } + ) + } +} + extension Project { public var allFiles: [Path] { diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index f218c88b..295fdbb3 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path public struct SpecOptions: Equatable { @@ -120,3 +121,15 @@ extension SpecOptions: JSONObjectConvertible { generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? false } } + +extension SpecOptions: PathContaining { + + static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = source + + result = expandStringPaths(from: result, forKey: "carthageBuildPath", relativeTo: path) + result = expandStringPaths(from: result, forKey: "carthageExecutablePath", relativeTo: path) + result = expandStringPaths(from: result, forKey: "defaultConfig", relativeTo: path) + return result + } +} diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index b7727e66..3cd7aed4 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -1,5 +1,6 @@ import Foundation import JSONUtilities +import struct PathKit.Path import xcodeproj public struct LegacyTarget: Equatable { @@ -110,6 +111,30 @@ extension Target: CustomStringConvertible { } } +extension Target: PathContaining { + + static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { + var result = source + for (targetName, var target) in result { + + // sources can either be an array of strings or an array of objects, so attempt to expand both + target = expandStringPaths(from: target, forKey: "sources", relativeTo: path) + target = expandChildPaths(from: target, forKey: "sources", relativeTo: path, type: TargetSource.self) + + target = expandStringPaths(from: target, forKey: "configFiles", relativeTo: path) + target = expandChildPaths(from: target, forKey: "dependencies", relativeTo: path, type: Dependency.self) + target = expandChildPaths(from: target, forKey: "info", relativeTo: path, type: Plist.self) + target = expandChildPaths(from: target, forKey: "entitlements", relativeTo: path, type: Plist.self) + target = expandChildPaths(from: target, forPotentialKeys: ["preBuildScripts", "prebuildScripts"], relativeTo: path, type: BuildScript.self) + target = expandChildPaths(from: target, forKey: "postCompileScripts", relativeTo: path, type: BuildScript.self) + target = expandChildPaths(from: target, forKey: "postBuildScripts", relativeTo: path, type: BuildScript.self) + + result[targetName] = target + } + return result + } +} + extension Target { static func resolveTargetTemplates(jsonDictionary: JSONDictionary) throws -> JSONDictionary { diff --git a/Sources/ProjectSpec/TargetSource.swift b/Sources/ProjectSpec/TargetSource.swift index fe51e445..4ad78dde 100644 --- a/Sources/ProjectSpec/TargetSource.swift +++ b/Sources/ProjectSpec/TargetSource.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import PathKit +import struct PathKit.Path import xcodeproj public struct TargetSource: Equatable { @@ -207,3 +207,14 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible { phaseOrder = .postCompile } } + +extension TargetSource: PathContaining { + + static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = source + + result = expandStringPaths(from: result, forKey: "path", relativeTo: path) + result = expandStringPaths(from: result, forKey: "excludes", relativeTo: path) + return result + } +} From a7e155411559033ff9ffabe5320808f2c423a546 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:22:56 +0000 Subject: [PATCH 08/29] Expand paths when initializing the Project model from a Spec --- Sources/ProjectSpec/Project.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 2f948f26..582c1444 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -135,6 +135,7 @@ extension Project { public init(spec: Spec, basePath: Path) throws { self.basePath = basePath + let spec = Project.expandPaths(for: spec) let jsonDictionary = try Project.resolveProject(jsonDictionary: spec.resolvedDictionary()) name = try jsonDictionary.json(atKeyPath: "name") From b144db3d70bcf8c50ba7e9c6847b039ec38fa8ec Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 13 Jan 2019 18:23:38 +0000 Subject: [PATCH 09/29] Add test and modify fixtures for verifying paths are expanded correctly --- Tests/Fixtures/paths_test.yml | 26 +++++- .../paths_test/included_paths_test.yml | 29 +++++- .../recursive_test/recursive_test.yml | 33 +++++++ Tests/XcodeGenKitTests/SpecLoadingTests.swift | 90 +++++++++++++++++-- 4 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 Tests/Fixtures/paths_test/recursive_test/recursive_test.yml diff --git a/Tests/Fixtures/paths_test.yml b/Tests/Fixtures/paths_test.yml index 3f209401..2cc63148 100644 --- a/Tests/Fixtures/paths_test.yml +++ b/Tests/Fixtures/paths_test.yml @@ -1,8 +1,32 @@ include: paths_test/included_paths_test.yml name: NewName +configFiles: + NewConfig: config targets: NewTarget: type: application platform: iOS + configFiles: + Config: config sources: - - $(CURDIR)/source + - source + dependencies: + - framework: Framework + info: + path: info + entitlements: + path: entitlements + preBuildScripts: + - path: preBuildScript + postCompileScripts: + - path: postCompileScript + postBuildScripts: + - path: postBuildScript +aggregateTargets: + NewAggregateTarget: + targets: + - NewTarget + configFiles: + Config: config + buildScripts: + - path: buildScript diff --git a/Tests/Fixtures/paths_test/included_paths_test.yml b/Tests/Fixtures/paths_test/included_paths_test.yml index f04bece6..aade9526 100644 --- a/Tests/Fixtures/paths_test/included_paths_test.yml +++ b/Tests/Fixtures/paths_test/included_paths_test.yml @@ -1,6 +1,31 @@ +include: recursive_test/recursive_test.yml +configFiles: + IncludedConfig: config targets: IncludedTarget: type: application - platform: iOS + platform: tvOS + configFiles: + Config: config sources: - - $(CURDIR)/source + - source + dependencies: + - framework: Framework + info: + path: info + entitlements: + path: entitlements + preBuildScripts: + - path: preBuildScript + postCompileScripts: + - path: postCompileScript + postBuildScripts: + - path: postBuildScript +aggregateTargets: + IncludedAggregateTarget: + targets: + - IncludedTarget + configFiles: + Config: config + buildScripts: + - path: buildScript diff --git a/Tests/Fixtures/paths_test/recursive_test/recursive_test.yml b/Tests/Fixtures/paths_test/recursive_test/recursive_test.yml new file mode 100644 index 00000000..6dc9a86f --- /dev/null +++ b/Tests/Fixtures/paths_test/recursive_test/recursive_test.yml @@ -0,0 +1,33 @@ +options: + carthageBuildPath: carthage_build + carthageExecutablePath: carthage_executable +configFiles: + RecursiveConfig: config +targets: + RecursiveTarget: + type: application + platform: macOS + configFiles: + Config: config + sources: + - source + dependencies: + - framework: Framework + info: + path: info + entitlements: + path: entitlements + prebuildScripts: + - path: prebuildScript + postCompileScripts: + - path: postCompileScript + postBuildScripts: + - path: postBuildScript +aggregateTargets: + RecursiveAggregateTarget: + targets: + - RecursiveTarget + configFiles: + Config: config + buildScripts: + - path: buildScript diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 7a13de6b..270d14cf 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -10,14 +10,6 @@ class SpecLoadingTests: XCTestCase { func testSpecLoader() { describe { - $0.it("expands directories") { - let path = fixturePath + "paths_test.yml" - let project = try Project(path: path) - - try expect(project.targets.first!.sources.first!.path) == (fixturePath + "paths_test" + "source").string - try expect(project.targets.last!.sources.first!.path) == (fixturePath + "source").string - } - $0.it("merges includes") { let path = fixturePath + "include_test.yml" let project = try Project(path: path) @@ -34,6 +26,85 @@ class SpecLoadingTests: XCTestCase { ] } + $0.it("expands directories") { + let path = fixturePath + "paths_test.yml" + let project = try Project(path: path) + + try expect(project.configFiles) == [ + "IncludedConfig": "paths_test/config", + "NewConfig": "config", + "RecursiveConfig": "paths_test/recursive_test/config", + ] + + try expect(project.options) == SpecOptions( + carthageBuildPath: "paths_test/recursive_test/carthage_build", + carthageExecutablePath: "paths_test/recursive_test/carthage_executable" + ) + + try expect(project.aggregateTargets) == [ + AggregateTarget( + name: "IncludedAggregateTarget", + targets: ["IncludedTarget"], + configFiles: ["Config": "paths_test/config"], + buildScripts: [BuildScript(script: .path("paths_test/buildScript"))] + ), + AggregateTarget( + name: "NewAggregateTarget", + targets: ["NewTarget"], + configFiles: ["Config": "config"], + buildScripts: [BuildScript(script: .path("buildScript"))] + ), + AggregateTarget( + name: "RecursiveAggregateTarget", + targets: ["RecursiveTarget"], + configFiles: ["Config": "paths_test/recursive_test/config"], + buildScripts: [BuildScript(script: .path("paths_test/recursive_test/buildScript"))] + ), + ] + + try expect(project.targets) == [ + Target( + name: "IncludedTarget", + type: .application, + platform: .tvOS, + configFiles: ["Config": "paths_test/config"], + sources: ["paths_test/source"], + dependencies: [Dependency(type: .framework, reference: "paths_test/Framework")], + info: Plist(path: "paths_test/info"), + entitlements: Plist(path: "paths_test/entitlements"), + preBuildScripts: [BuildScript(script: .path("paths_test/preBuildScript"))], + postCompileScripts: [BuildScript(script: .path("paths_test/postCompileScript"))], + postBuildScripts: [BuildScript(script: .path("paths_test/postBuildScript"))] + ), + Target( + name: "NewTarget", + type: .application, + platform: .iOS, + configFiles: ["Config": "config"], + sources: ["source"], + dependencies: [Dependency(type: .framework, reference: "Framework")], + info: Plist(path: "info"), + entitlements: Plist(path: "entitlements"), + preBuildScripts: [BuildScript(script: .path("preBuildScript"))], + postCompileScripts: [BuildScript(script: .path("postCompileScript"))], + postBuildScripts: [BuildScript(script: .path("postBuildScript"))] + ), + Target( + name: "RecursiveTarget", + type: .application, + platform: .macOS, + configFiles: ["Config": "paths_test/recursive_test/config"], + sources: ["paths_test/recursive_test/source"], + dependencies: [Dependency(type: .framework, reference: "paths_test/recursive_test/Framework")], + info: Plist(path: "paths_test/recursive_test/info"), + entitlements: Plist(path: "paths_test/recursive_test/entitlements"), + preBuildScripts: [BuildScript(script: .path("paths_test/recursive_test/prebuildScript"))], + postCompileScripts: [BuildScript(script: .path("paths_test/recursive_test/postCompileScript"))], + postBuildScripts: [BuildScript(script: .path("paths_test/recursive_test/postBuildScript"))] + ), + ] + } + $0.it("parses yaml types") { let path = fixturePath + "yaml.yml" let dictionary = try loadYamlDictionary(path: path) @@ -541,7 +612,8 @@ fileprivate func getProjectSpec(_ project: [String: Any], file: String = #file, projectDictionary[key] = value } do { - return try Project(basePath: "", jsonDictionary: projectDictionary) + let template = Project.Spec(relativePath: "", jsonDictionary: projectDictionary) + return try Project(spec: template, basePath: "") } catch { throw failure("\(error)", file: file, line: line) } From 8f4c0953b3a2418eb9b737cbb56ff1485a431883 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Tue, 15 Jan 2019 12:23:52 +0000 Subject: [PATCH 10/29] Add back deleted public functions and deprecate (prevent breaking public API) --- Sources/ProjectSpec/Project.swift | 6 ++++++ Sources/ProjectSpec/SpecLoader.swift | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 582c1444..0b5d8774 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -132,6 +132,12 @@ extension Project: Equatable { extension Project { + @available(*, deprecated, message: "Use `Project.Spec` and `init(spec:basePath:)`.") + public init(basePath: Path, jsonDictionary: JSONDictionary) throws { + let spec = Project.Spec(relativePath: Path(), jsonDictionary: jsonDictionary) + try self.init(spec: spec, basePath: basePath) + } + public init(spec: Spec, basePath: Path) throws { self.basePath = basePath diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index f5cb839e..c2eb8004 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -10,6 +10,11 @@ extension Project { let template = try Spec(filename: path.lastComponent, basePath: basePath) try self.init(spec: template, basePath: basePath) } + + @available(*, deprecated, message: "Use `Project.Spec` for loading files from disk.") + public static func loadDictionary(path: Path) throws -> JSONDictionary { + return try Project.Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary + } } protocol PathContaining { From ccf002f8bf9fbb18e3784e3429d28f47bd8534b7 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Tue, 15 Jan 2019 12:27:13 +0000 Subject: [PATCH 11/29] Consistent default value and guard check --- Sources/ProjectSpec/Project.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 0b5d8774..f912fd5d 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -177,7 +177,7 @@ extension Project { extension Project: PathContaining { - static func expandPaths(for spec: Spec, relativeTo basePath: Path = "") -> Spec { + static func expandPaths(for spec: Spec, relativeTo basePath: Path = Path()) -> Spec { let relativePath = (basePath + spec.relativePath).normalize() guard relativePath != Path() else { return spec From 05a564c32497204e875d911a392a9d2479360067 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Mon, 21 Jan 2019 13:12:14 +0000 Subject: [PATCH 12/29] Use normal imports --- Sources/ProjectSpec/AggregateTarget.swift | 2 +- Sources/ProjectSpec/BuildScript.swift | 2 +- Sources/ProjectSpec/Dependency.swift | 2 +- Sources/ProjectSpec/Plist.swift | 2 +- Sources/ProjectSpec/SpecOptions.swift | 2 +- Sources/ProjectSpec/Target.swift | 2 +- Sources/ProjectSpec/TargetSource.swift | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index 27c445bc..976581bf 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit public struct AggregateTarget: ProjectTarget { public var name: String diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index 6d3315bd..ac99a412 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit public struct BuildScript: Equatable { diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 76641d8f..a74bdbda 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit public struct Dependency: Equatable { diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index d66721a2..455291f8 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit public struct Plist: Equatable { diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index 295fdbb3..fa78fd10 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit public struct SpecOptions: Equatable { diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index 3cd7aed4..ca9c293c 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit import xcodeproj public struct LegacyTarget: Equatable { diff --git a/Sources/ProjectSpec/TargetSource.swift b/Sources/ProjectSpec/TargetSource.swift index 4ad78dde..960315f7 100644 --- a/Sources/ProjectSpec/TargetSource.swift +++ b/Sources/ProjectSpec/TargetSource.swift @@ -1,6 +1,6 @@ import Foundation import JSONUtilities -import struct PathKit.Path +import PathKit import xcodeproj public struct TargetSource: Equatable { From 151d78ef98e28a289c90b451063f851fa8057274 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Mon, 21 Jan 2019 13:32:13 +0000 Subject: [PATCH 13/29] Rename PathContaining -> PathContainer --- Sources/ProjectSpec/AggregateTarget.swift | 2 +- Sources/ProjectSpec/BuildScript.swift | 2 +- Sources/ProjectSpec/Dependency.swift | 2 +- Sources/ProjectSpec/Plist.swift | 2 +- Sources/ProjectSpec/Project.swift | 2 +- Sources/ProjectSpec/SpecLoader.swift | 8 ++++---- Sources/ProjectSpec/SpecOptions.swift | 2 +- Sources/ProjectSpec/Target.swift | 2 +- Sources/ProjectSpec/TargetSource.swift | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index 976581bf..c3a983fc 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -63,7 +63,7 @@ extension AggregateTarget: NamedJSONDictionaryConvertible { } } -extension AggregateTarget: PathContaining { +extension AggregateTarget: PathContainer { static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { var result = source diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index ac99a412..67340f55 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -55,7 +55,7 @@ extension BuildScript: JSONObjectConvertible { } } -extension BuildScript: PathContaining { +extension BuildScript: PathContainer { static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { return expandStringPaths(from: source, forKey: "path", relativeTo: path) diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index a74bdbda..94ae0d87 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -74,7 +74,7 @@ extension Dependency: JSONObjectConvertible { } } -extension Dependency: PathContaining { +extension Dependency: PathContainer { static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { return expandStringPaths(from: source, forKey: "framework", relativeTo: path) diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index 455291f8..d1f7c624 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -26,7 +26,7 @@ extension Plist: JSONObjectConvertible { } } -extension Plist: PathContaining { +extension Plist: PathContainer { static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { return expandStringPaths(from: source, forKey: "path", relativeTo: path) } diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index f912fd5d..2963dd4d 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -175,7 +175,7 @@ extension Project { } } -extension Project: PathContaining { +extension Project: PathContainer { static func expandPaths(for spec: Spec, relativeTo basePath: Path = Path()) -> Spec { let relativePath = (basePath + spec.relativePath).normalize() diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index c2eb8004..97d90385 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -17,13 +17,13 @@ extension Project { } } -protocol PathContaining { +protocol PathContainer { associatedtype JSONSourceType static func expandPaths(for source: JSONSourceType, relativeTo path: Path) -> JSONSourceType } -extension PathContaining { +extension PathContainer { static func expandStringPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path) -> JSONDictionary { var result = source @@ -38,7 +38,7 @@ extension PathContaining { return result } - static func expandChildPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path, type: T.Type) -> JSONDictionary { + static func expandChildPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path, type: T.Type) -> JSONDictionary { var result = source if let source = result[key] as? T.JSONSourceType { @@ -51,7 +51,7 @@ extension PathContaining { return result } - static func expandChildPaths(from source: JSONDictionary, forPotentialKeys keys: [String], relativeTo path: Path, type: T.Type) -> JSONDictionary { + static func expandChildPaths(from source: JSONDictionary, forPotentialKeys keys: [String], relativeTo path: Path, type: T.Type) -> JSONDictionary { var result = source for key in keys { diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index fa78fd10..6e372562 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -122,7 +122,7 @@ extension SpecOptions: JSONObjectConvertible { } } -extension SpecOptions: PathContaining { +extension SpecOptions: PathContainer { static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { var result = source diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index ca9c293c..b1ebf349 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -111,7 +111,7 @@ extension Target: CustomStringConvertible { } } -extension Target: PathContaining { +extension Target: PathContainer { static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { var result = source diff --git a/Sources/ProjectSpec/TargetSource.swift b/Sources/ProjectSpec/TargetSource.swift index 960315f7..d19d920d 100644 --- a/Sources/ProjectSpec/TargetSource.swift +++ b/Sources/ProjectSpec/TargetSource.swift @@ -208,7 +208,7 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible { } } -extension TargetSource: PathContaining { +extension TargetSource: PathContainer { static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { var result = source From d2579bfe436e3c89861011c4a53f58d462c7d0b8 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Thu, 24 Jan 2019 12:50:34 +0000 Subject: [PATCH 14/29] Refactor the PathContainer protocol to be a bit more expressive --- Sources/ProjectSpec/AggregateTarget.swift | 16 +++---- Sources/ProjectSpec/BuildScript.swift | 6 ++- Sources/ProjectSpec/Dependency.swift | 8 ++-- Sources/ProjectSpec/Plist.swift | 7 ++- Sources/ProjectSpec/Project.swift | 53 ++++++++++++++++++++--- Sources/ProjectSpec/SpecLoader.swift | 43 +++--------------- Sources/ProjectSpec/SpecOptions.swift | 13 +++--- Sources/ProjectSpec/Target.swift | 34 +++++++-------- Sources/ProjectSpec/TargetSource.swift | 11 +++-- 9 files changed, 99 insertions(+), 92 deletions(-) diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index c3a983fc..06c3beb9 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -65,14 +65,12 @@ extension AggregateTarget: NamedJSONDictionaryConvertible { extension AggregateTarget: PathContainer { - static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { - var result = source - - for (targetName, var target) in result { - target = expandStringPaths(from: target, forKey: "configFiles", relativeTo: path) - target = expandChildPaths(from: target, forKey: "buildScripts", relativeTo: path, type: BuildScript.self) - result[targetName] = target - } - return result + static var pathProperties: [PathProperty] { + return [ + .dictionary([ + .string("configFiles"), + .object("buildScripts", BuildScript.pathProperties), + ]), + ] } } diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index 67340f55..1a346e4e 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -57,7 +57,9 @@ extension BuildScript: JSONObjectConvertible { extension BuildScript: PathContainer { - static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - return expandStringPaths(from: source, forKey: "path", relativeTo: path) + static var pathProperties: [PathProperty] { + return [ + .string("path"), + ] } } diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 94ae0d87..1a214258 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -75,8 +75,10 @@ extension Dependency: JSONObjectConvertible { } extension Dependency: PathContainer { - - static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - return expandStringPaths(from: source, forKey: "framework", relativeTo: path) + + static var pathProperties: [PathProperty] { + return [ + .string("framework"), + ] } } diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index d1f7c624..c3a31bbf 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -27,7 +27,10 @@ extension Plist: JSONObjectConvertible { } extension Plist: PathContainer { - static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - return expandStringPaths(from: source, forKey: "path", relativeTo: path) + + static var pathProperties: [PathProperty] { + return [ + .string("path"), + ] } } diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 2963dd4d..2bef7874 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -177,18 +177,26 @@ extension Project { extension Project: PathContainer { + static var pathProperties: [PathProperty] { + return [ + .string("configFiles"), + .object("options", SpecOptions.pathProperties), + .object("targets", Target.pathProperties), + .object("aggregateTargets", AggregateTarget.pathProperties), + ] + } +} + +extension Project { + static func expandPaths(for spec: Spec, relativeTo basePath: Path = Path()) -> Spec { let relativePath = (basePath + spec.relativePath).normalize() guard relativePath != Path() else { return spec } - - var jsonDictionary = spec.jsonDictionary - jsonDictionary = expandStringPaths(from: jsonDictionary, forKey: "configFiles", relativeTo: relativePath) - jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "options", relativeTo: relativePath, type: SpecOptions.self) - jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "targets", relativeTo: relativePath, type: Target.self) - jsonDictionary = expandChildPaths(from: jsonDictionary, forKey: "aggregateTargets", relativeTo: relativePath, type: AggregateTarget.self) + let jsonDictionary = expandPaths(Project.pathProperties, in: spec.jsonDictionary, relativeTo: relativePath) + return Spec( relativePath: spec.relativePath, jsonDictionary: jsonDictionary, @@ -197,6 +205,39 @@ extension Project: PathContainer { } ) } + + private static func expandPaths(_ pathProperties: [PathProperty], in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = jsonDictionary + + for pathProperty in pathProperties { + switch pathProperty { + case .string(let key): + if let source = result[key] as? String { + result[key] = (path + source).string + } else if let source = result[key] as? [String] { + result[key] = source.map { (path + $0).string } + } else if let source = result[key] as? [String: String] { + result[key] = source.mapValues { (path + $0).string } + } + case .dictionary(let pathProperties): + for (key, dictionary) in result { + if let source = dictionary as? JSONDictionary { + result[key] = expandPaths(pathProperties, in: source, relativeTo: path) + } + } + case .object(let key, let pathProperties): + if let source = result[key] as? JSONDictionary { + result[key] = expandPaths(pathProperties, in: source, relativeTo: path) + } else if let source = result[key] as? [JSONDictionary] { + result[key] = source.map { expandPaths(pathProperties, in: $0, relativeTo: path) } + } else if let source = result[key] as? [String: JSONDictionary] { + result[key] = source.mapValues { expandPaths(pathProperties, in: $0, relativeTo: path) } + } + } + } + + return result + } } extension Project { diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 97d90385..504678d5 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -19,44 +19,11 @@ extension Project { protocol PathContainer { - associatedtype JSONSourceType - static func expandPaths(for source: JSONSourceType, relativeTo path: Path) -> JSONSourceType + static var pathProperties: [PathProperty] { get } } -extension PathContainer { - - static func expandStringPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path) -> JSONDictionary { - var result = source - - if let source = result[key] as? String { - result[key] = (path + source).string - } else if let source = result[key] as? [String] { - result[key] = source.map { (path + $0).string } - } else if let source = result[key] as? [String: String] { - result[key] = source.mapValues { (path + $0).string } - } - return result - } - - static func expandChildPaths(from source: JSONDictionary, forKey key: String, relativeTo path: Path, type: T.Type) -> JSONDictionary { - var result = source - - if let source = result[key] as? T.JSONSourceType { - result[key] = T.expandPaths(for: source, relativeTo: path) - } else if let source = result[key] as? [T.JSONSourceType] { - result[key] = source.map { T.expandPaths(for: $0, relativeTo: path) } - } else if let source = result[key] as? [String: T.JSONSourceType] { - result[key] = source.mapValues { T.expandPaths(for: $0, relativeTo: path) } - } - return result - } - - static func expandChildPaths(from source: JSONDictionary, forPotentialKeys keys: [String], relativeTo path: Path, type: T.Type) -> JSONDictionary { - var result = source - - for key in keys { - result = expandChildPaths(from: result, forKey: key, relativeTo: path, type: type) - } - return result - } +enum PathProperty { + case string(String) + case dictionary([PathProperty]) + case object(String, [PathProperty]) } diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index 6e372562..591fd2a3 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -123,13 +123,12 @@ extension SpecOptions: JSONObjectConvertible { } extension SpecOptions: PathContainer { - - static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - var result = source - result = expandStringPaths(from: result, forKey: "carthageBuildPath", relativeTo: path) - result = expandStringPaths(from: result, forKey: "carthageExecutablePath", relativeTo: path) - result = expandStringPaths(from: result, forKey: "defaultConfig", relativeTo: path) - return result + static var pathProperties: [PathProperty] { + return [ + .string("carthageBuildPath"), + .string("carthageExecutablePath"), + .string("defaultConfig"), + ] } } diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index b1ebf349..f5cacaab 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -113,25 +113,21 @@ extension Target: CustomStringConvertible { extension Target: PathContainer { - static func expandPaths(for source: [String: JSONDictionary], relativeTo path: Path) -> [String: JSONDictionary] { - var result = source - for (targetName, var target) in result { - - // sources can either be an array of strings or an array of objects, so attempt to expand both - target = expandStringPaths(from: target, forKey: "sources", relativeTo: path) - target = expandChildPaths(from: target, forKey: "sources", relativeTo: path, type: TargetSource.self) - - target = expandStringPaths(from: target, forKey: "configFiles", relativeTo: path) - target = expandChildPaths(from: target, forKey: "dependencies", relativeTo: path, type: Dependency.self) - target = expandChildPaths(from: target, forKey: "info", relativeTo: path, type: Plist.self) - target = expandChildPaths(from: target, forKey: "entitlements", relativeTo: path, type: Plist.self) - target = expandChildPaths(from: target, forPotentialKeys: ["preBuildScripts", "prebuildScripts"], relativeTo: path, type: BuildScript.self) - target = expandChildPaths(from: target, forKey: "postCompileScripts", relativeTo: path, type: BuildScript.self) - target = expandChildPaths(from: target, forKey: "postBuildScripts", relativeTo: path, type: BuildScript.self) - - result[targetName] = target - } - return result + static var pathProperties: [PathProperty] { + return [ + .dictionary([ + .string("sources"), + .object("sources", TargetSource.pathProperties), + .string("configFiles"), + .object("dependencies", Dependency.pathProperties), + .object("info", Plist.pathProperties), + .object("entitlements", Plist.pathProperties), + .object("preBuildScripts", BuildScript.pathProperties), + .object("prebuildScripts", BuildScript.pathProperties), + .object("postCompileScripts", BuildScript.pathProperties), + .object("postBuildScripts", BuildScript.pathProperties), + ]), + ] } } diff --git a/Sources/ProjectSpec/TargetSource.swift b/Sources/ProjectSpec/TargetSource.swift index d19d920d..950142b1 100644 --- a/Sources/ProjectSpec/TargetSource.swift +++ b/Sources/ProjectSpec/TargetSource.swift @@ -210,11 +210,10 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible { extension TargetSource: PathContainer { - static func expandPaths(for source: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - var result = source - - result = expandStringPaths(from: result, forKey: "path", relativeTo: path) - result = expandStringPaths(from: result, forKey: "excludes", relativeTo: path) - return result + static var pathProperties: [PathProperty] { + return [ + .string("path"), + .string("excludes"), + ] } } From 6589b840f667a2bd175c34806a47249225b94a79 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Thu, 24 Jan 2019 13:46:55 +0000 Subject: [PATCH 15/29] Add option to revert to legacy path behaviour --- Sources/ProjectSpec/ProjectSpec.swift | 34 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift index b0566fec..2b30ab05 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -15,7 +15,7 @@ extension Project { self.subSpecs = subSpecs } - public init(filename: String, basePath: Path, relativePath: Path = "") throws { + public init(filename: String, basePath: Path, relativePath: Path = Path()) throws { let path = basePath + relativePath + filename // Depending on the extension we will either load the file as YAML or JSON @@ -31,18 +31,30 @@ extension Project { json = try loadYamlDictionary(path: path) } - var includeStrings: [String] - if let includeString = json["include"] as? String { - includeStrings = [includeString] - } else if let includeArray = json["include"] as? [String] { - includeStrings = includeArray - } else { - includeStrings = [] + let processIncludeOption = { (option: Any) -> (String, Bool)? in + if let option = option as? String { + return (option, true) + } else if let option = option as? JSONDictionary, let path = option["path"] as? String { + return (path, (option["useRelativePaths"] as? Bool) ?? true) + } + return nil } - let includes = try includeStrings.map { include -> Spec in - let path = Path(include) - return try Spec(filename: path.lastComponent, basePath: basePath + relativePath, relativePath: path.parent()) + let includeSources: [(String, Bool)] + if let sources = json["include"] as? [Any] { + includeSources = sources.compactMap { processIncludeOption($0) } + } else if let source = json["include"] { + includeSources = [processIncludeOption(source)].compactMap { $0 } + } else { + includeSources = [] + } + + let includes = try includeSources.map { include -> Spec in + let path = Path(include.0) + let basePath = include.1 ? basePath + relativePath : basePath + relativePath + path.parent() + let relativePath = include.1 ? path.parent() : Path() + + return try Spec(filename: path.lastComponent, basePath: basePath, relativePath: relativePath) } self.relativePath = relativePath From 16a02fa1a68ccf0eb9673bb429ba993fdd1f6a4c Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Thu, 24 Jan 2019 13:47:21 +0000 Subject: [PATCH 16/29] Add tests for legacy path behavior --- Tests/Fixtures/legacy_paths_test.yml | 4 ++ .../legacy_included_paths_test.yml | 33 ++++++++++++++++ Tests/XcodeGenKitTests/SpecLoadingTests.swift | 39 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 Tests/Fixtures/legacy_paths_test.yml create mode 100644 Tests/Fixtures/legacy_paths_test/legacy_included_paths_test.yml diff --git a/Tests/Fixtures/legacy_paths_test.yml b/Tests/Fixtures/legacy_paths_test.yml new file mode 100644 index 00000000..50b40b1b --- /dev/null +++ b/Tests/Fixtures/legacy_paths_test.yml @@ -0,0 +1,4 @@ +include: + path: legacy_paths_test/legacy_included_paths_test.yml + useRelativePaths: false +name: NewName diff --git a/Tests/Fixtures/legacy_paths_test/legacy_included_paths_test.yml b/Tests/Fixtures/legacy_paths_test/legacy_included_paths_test.yml new file mode 100644 index 00000000..de8cce3c --- /dev/null +++ b/Tests/Fixtures/legacy_paths_test/legacy_included_paths_test.yml @@ -0,0 +1,33 @@ +configFiles: + IncludedConfig: config +options: + carthageBuildPath: carthage_build + carthageExecutablePath: carthage_executable +targets: + IncludedTarget: + type: application + platform: tvOS + configFiles: + Config: config + sources: + - source + dependencies: + - framework: Framework + info: + path: info + entitlements: + path: entitlements + preBuildScripts: + - path: preBuildScript + postCompileScripts: + - path: postCompileScript + postBuildScripts: + - path: postBuildScript +aggregateTargets: + IncludedAggregateTarget: + targets: + - IncludedTarget + configFiles: + Config: config + buildScripts: + - path: buildScript diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 270d14cf..a2048c8b 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -105,6 +105,45 @@ class SpecLoadingTests: XCTestCase { ] } + $0.it("respects directory expansion preference") { + let path = fixturePath + "legacy_paths_test.yml" + let project = try Project(path: path) + + try expect(project.configFiles) == [ + "IncludedConfig": "config", + ] + + try expect(project.options) == SpecOptions( + carthageBuildPath: "carthage_build", + carthageExecutablePath: "carthage_executable" + ) + + try expect(project.aggregateTargets) == [ + AggregateTarget( + name: "IncludedAggregateTarget", + targets: ["IncludedTarget"], + configFiles: ["Config": "config"], + buildScripts: [BuildScript(script: .path("buildScript"))] + ), + ] + + try expect(project.targets) == [ + Target( + name: "IncludedTarget", + type: .application, + platform: .tvOS, + configFiles: ["Config": "config"], + sources: ["source"], + dependencies: [Dependency(type: .framework, reference: "Framework")], + info: Plist(path: "info"), + entitlements: Plist(path: "entitlements"), + preBuildScripts: [BuildScript(script: .path("preBuildScript"))], + postCompileScripts: [BuildScript(script: .path("postCompileScript"))], + postBuildScripts: [BuildScript(script: .path("postBuildScript"))] + ), + ] + } + $0.it("parses yaml types") { let path = fixturePath + "yaml.yml" let dictionary = try loadYamlDictionary(path: path) From 20438591890a582fe15567424d66c390af4d2dc5 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Thu, 24 Jan 2019 14:03:45 +0000 Subject: [PATCH 17/29] Update documentation for new include object behaviour --- Docs/ProjectSpec.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index b84efa11..6baf51e5 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -45,7 +45,22 @@ Required properties are marked with checkbox. Some of the YAML examples don't sh One or more specs can be included in the project spec. This can be used to split your project spec into multiple files, for easier structuring or sharing between multiple specs. Included specs can also include other specs and so on. -Include can either be a list of string paths or a single string path. They will be merged in order and then the current spec will be merged on top. +Include can either be a list of includes or a single include. They will be merged in order and then the current spec will be merged on top. + +An include can be provided via a string (the path) or an object of the form: + +**Include Object** + +- [x] **path**: **String** - The path to the included file. +- [ ] **useRelativePaths**: **Bool** - Dictates whether the included spec specifies paths relative to itself (the default) or the root spec file. + +```yaml +include: + - includedFile.yml + - path: path/to/includedFile.yml + useRelativePaths: false +``` + By default specs are merged additively. That is for every value: - if existing value and new value are both dictionaries merge them and continue down the hierachy From 6628c382ac381326d38971b2ad852a8e30ed2998 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:22:46 +0000 Subject: [PATCH 18/29] Rename useRelativePaths --> relativePaths --- Docs/ProjectSpec.md | 4 ++-- Sources/ProjectSpec/ProjectSpec.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 6baf51e5..465954bf 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -52,13 +52,13 @@ An include can be provided via a string (the path) or an object of the form: **Include Object** - [x] **path**: **String** - The path to the included file. -- [ ] **useRelativePaths**: **Bool** - Dictates whether the included spec specifies paths relative to itself (the default) or the root spec file. +- [ ] **relativePaths**: **Bool** - Dictates whether the included spec specifies paths relative to itself (the default) or the root spec file. ```yaml include: - includedFile.yml - path: path/to/includedFile.yml - useRelativePaths: false + relativePaths: false ``` By default specs are merged additively. That is for every value: diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift index 2b30ab05..e26d1275 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -35,7 +35,7 @@ extension Project { if let option = option as? String { return (option, true) } else if let option = option as? JSONDictionary, let path = option["path"] as? String { - return (path, (option["useRelativePaths"] as? Bool) ?? true) + return (path, (option["relativePaths"] as? Bool) ?? true) } return nil } From 4d458ad6e4b6eb4d87fedf1f265c18bfdad1735f Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:23:48 +0000 Subject: [PATCH 19/29] Simplify dictionary extension --- Sources/ProjectSpec/ProjectSpec.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift index e26d1275..d4b78e73 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -72,15 +72,7 @@ extension Project { } } -internal protocol SubSequenceConvertible: Sequence { - - init(_ subsequence: SubSequence) -} - -extension String: SubSequenceConvertible { } -extension Substring: SubSequenceConvertible { } - -extension Dictionary where Key: StringProtocol, Key: SubSequenceConvertible, Value: Any { +extension Dictionary where Key == String, Value: Any { func merged(onto other: Dictionary) -> Dictionary { var merged = other From 25a4d48b83dae353836c5f1be9ddbbd5d87c8db1 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:24:16 +0000 Subject: [PATCH 20/29] Remove deprecations --- Sources/ProjectSpec/Project.swift | 1 - Sources/ProjectSpec/SpecLoader.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 2bef7874..b113c51c 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -132,7 +132,6 @@ extension Project: Equatable { extension Project { - @available(*, deprecated, message: "Use `Project.Spec` and `init(spec:basePath:)`.") public init(basePath: Path, jsonDictionary: JSONDictionary) throws { let spec = Project.Spec(relativePath: Path(), jsonDictionary: jsonDictionary) try self.init(spec: spec, basePath: basePath) diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 504678d5..5744de60 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -11,7 +11,6 @@ extension Project { try self.init(spec: template, basePath: basePath) } - @available(*, deprecated, message: "Use `Project.Spec` for loading files from disk.") public static func loadDictionary(path: Path) throws -> JSONDictionary { return try Project.Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary } From 607e75f402c7944dbc7da0a8a5e3e7a0c8cefaa5 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:33:56 +0000 Subject: [PATCH 21/29] Fix legacy path expansion test --- Tests/Fixtures/legacy_paths_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Fixtures/legacy_paths_test.yml b/Tests/Fixtures/legacy_paths_test.yml index 50b40b1b..9ee86ce2 100644 --- a/Tests/Fixtures/legacy_paths_test.yml +++ b/Tests/Fixtures/legacy_paths_test.yml @@ -1,4 +1,4 @@ include: path: legacy_paths_test/legacy_included_paths_test.yml - useRelativePaths: false + relativePaths: false name: NewName From 71a7ec058871224b26d9510b5f78339ae5e22570 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:34:17 +0000 Subject: [PATCH 22/29] Move path expansion logic into the spec file --- Sources/ProjectSpec/Project.swift | 55 +------------------------- Sources/ProjectSpec/ProjectSpec.swift | 56 ++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index b113c51c..a17a240c 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -140,7 +140,7 @@ extension Project { public init(spec: Spec, basePath: Path) throws { self.basePath = basePath - let spec = Project.expandPaths(for: spec) + let spec = spec.resolvingPaths() let jsonDictionary = try Project.resolveProject(jsonDictionary: spec.resolvedDictionary()) name = try jsonDictionary.json(atKeyPath: "name") @@ -186,59 +186,6 @@ extension Project: PathContainer { } } -extension Project { - - static func expandPaths(for spec: Spec, relativeTo basePath: Path = Path()) -> Spec { - let relativePath = (basePath + spec.relativePath).normalize() - guard relativePath != Path() else { - return spec - } - - let jsonDictionary = expandPaths(Project.pathProperties, in: spec.jsonDictionary, relativeTo: relativePath) - - return Spec( - relativePath: spec.relativePath, - jsonDictionary: jsonDictionary, - subSpecs: spec.subSpecs.map { template in - return Project.expandPaths(for: template, relativeTo: relativePath) - } - ) - } - - private static func expandPaths(_ pathProperties: [PathProperty], in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - var result = jsonDictionary - - for pathProperty in pathProperties { - switch pathProperty { - case .string(let key): - if let source = result[key] as? String { - result[key] = (path + source).string - } else if let source = result[key] as? [String] { - result[key] = source.map { (path + $0).string } - } else if let source = result[key] as? [String: String] { - result[key] = source.mapValues { (path + $0).string } - } - case .dictionary(let pathProperties): - for (key, dictionary) in result { - if let source = dictionary as? JSONDictionary { - result[key] = expandPaths(pathProperties, in: source, relativeTo: path) - } - } - case .object(let key, let pathProperties): - if let source = result[key] as? JSONDictionary { - result[key] = expandPaths(pathProperties, in: source, relativeTo: path) - } else if let source = result[key] as? [JSONDictionary] { - result[key] = source.map { expandPaths(pathProperties, in: $0, relativeTo: path) } - } else if let source = result[key] as? [String: JSONDictionary] { - result[key] = source.mapValues { expandPaths(pathProperties, in: $0, relativeTo: path) } - } - } - } - - return result - } -} - extension Project { public var allFiles: [Path] { diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift index d4b78e73..d98dc68b 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -1,5 +1,5 @@ import Foundation -import typealias JSONUtilities.JSONDictionary +import JSONUtilities import PathKit extension Project { @@ -72,6 +72,60 @@ extension Project { } } +extension Project.Spec { + func resolvingPaths(relativeTo basePath: Path = Path()) -> Project.Spec { + let relativePath = (basePath + self.relativePath).normalize() + guard relativePath != Path() else { + return self + } + + let jsonDictionary = Project.pathProperties.resolvingPaths(in: self.jsonDictionary, relativeTo: relativePath) + + return Project.Spec( + relativePath: self.relativePath, + jsonDictionary: jsonDictionary, + subSpecs: self.subSpecs.map { template in + return template.resolvingPaths(relativeTo: relativePath) + } + ) + } +} + +extension Array where Element == PathProperty { + func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = jsonDictionary + + for pathProperty in self { + switch pathProperty { + case .string(let key): + if let source = result[key] as? String { + result[key] = (path + source).string + } else if let source = result[key] as? [String] { + result[key] = source.map { (path + $0).string } + } else if let source = result[key] as? [String: String] { + result[key] = source.mapValues { (path + $0).string } + } + case .dictionary(let pathProperties): + for (key, dictionary) in result { + if let source = dictionary as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } + } + case .object(let key, let pathProperties): + if let source = result[key] as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } else if let source = result[key] as? [JSONDictionary] { + result[key] = source.map { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } else if let source = result[key] as? [String: JSONDictionary] { + result[key] = source.mapValues { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } + } + } + + return result + } +} + extension Dictionary where Key == String, Value: Any { func merged(onto other: Dictionary) -> Dictionary { From 3c339b77ed9da7b6d863bd5902a9e973aff05146 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:41:09 +0000 Subject: [PATCH 23/29] Add changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c97a88df..aa49f6b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Added support for the `instrumentsPackage` product type [#482](https://github.com/yonaskolb/XcodeGen/pull/482) @ksulliva - Added support for `inputFileLists` and `outputFileLists` within project build scripts [#500](https://github.com/yonaskolb/XcodeGen/pull/500) @lukewakeford +#### Changed +- **BREAKING** All the paths within included files are now relative to that file and not the root spec. This can be disabled with a `relativePaths: false` on the include. See [docs](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#include) for more details [#489](https://github.com/yonaskolb/XcodeGen/pull/489) @ellneal + #### Fixed - Fixed `--project` argument not taking effect [#487](https://github.com/yonaskolb/XcodeGen/pull/487) @monowerker - Fixed Sticker Packs from generating an empty Source file phase which caused in error in the new build system [#492](https://github.com/yonaskolb/XcodeGen/pull/492) @rpassis From 1d62b46423e1ec021a02bf64f0b03395e26d6490 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 12:46:00 +0000 Subject: [PATCH 24/29] #whitespace --- Sources/ProjectSpec/SpecLoader.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 5744de60..4c0925e4 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -4,7 +4,7 @@ import PathKit import Yams extension Project { - + public init(path: Path) throws { let basePath = path.parent() let template = try Spec(filename: path.lastComponent, basePath: basePath) From 060aca83273e6f98c9ea09d706a0aa01452e639f Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 15:23:23 +0000 Subject: [PATCH 25/29] Rename Project.Spec --> Spec --- Sources/ProjectSpec/Project.swift | 2 +- .../{ProjectSpec.swift => Spec.swift} | 121 +++++++++--------- Sources/ProjectSpec/SpecLoader.swift | 2 +- Sources/XcodeGenKit/SpecLoader.swift | 2 +- Tests/XcodeGenKitTests/SpecLoadingTests.swift | 2 +- 5 files changed, 63 insertions(+), 66 deletions(-) rename Sources/ProjectSpec/{ProjectSpec.swift => Spec.swift} (52%) diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index a17a240c..c2c11b0f 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -133,7 +133,7 @@ extension Project: Equatable { extension Project { public init(basePath: Path, jsonDictionary: JSONDictionary) throws { - let spec = Project.Spec(relativePath: Path(), jsonDictionary: jsonDictionary) + let spec = Spec(relativePath: Path(), jsonDictionary: jsonDictionary) try self.init(spec: spec, basePath: basePath) } diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/Spec.swift similarity index 52% rename from Sources/ProjectSpec/ProjectSpec.swift rename to Sources/ProjectSpec/Spec.swift index d98dc68b..abff04d8 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/Spec.swift @@ -2,78 +2,75 @@ import Foundation import JSONUtilities import PathKit -extension Project { +public struct Spec { + public let relativePath: Path + public let jsonDictionary: JSONDictionary + public let subSpecs: [Spec] - public struct Spec { - public let relativePath: Path - public let jsonDictionary: JSONDictionary - public let subSpecs: [Spec] + public init(relativePath: Path, jsonDictionary: JSONDictionary, subSpecs: [Spec] = []) { + self.relativePath = relativePath + self.jsonDictionary = jsonDictionary + self.subSpecs = subSpecs + } - public init(relativePath: Path, jsonDictionary: JSONDictionary, subSpecs: [Spec] = []) { - self.relativePath = relativePath - self.jsonDictionary = jsonDictionary - self.subSpecs = subSpecs + public init(filename: String, basePath: Path, relativePath: Path = Path()) throws { + let path = basePath + relativePath + filename + + // Depending on the extension we will either load the file as YAML or JSON + var json: [String: Any] + if path.extension?.lowercased() == "json" { + let data: Data = try path.read() + let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) + guard let jsonDictionary = jsonData as? [String: Any] else { + fatalError("Invalid JSON at path \(path)") + } + json = jsonDictionary + } else { + json = try loadYamlDictionary(path: path) } - public init(filename: String, basePath: Path, relativePath: Path = Path()) throws { - let path = basePath + relativePath + filename - - // Depending on the extension we will either load the file as YAML or JSON - var json: [String: Any] - if path.extension?.lowercased() == "json" { - let data: Data = try path.read() - let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) - guard let jsonDictionary = jsonData as? [String: Any] else { - fatalError("Invalid JSON at path \(path)") - } - json = jsonDictionary - } else { - json = try loadYamlDictionary(path: path) + let processIncludeOption = { (option: Any) -> (String, Bool)? in + if let option = option as? String { + return (option, true) + } else if let option = option as? JSONDictionary, let path = option["path"] as? String { + return (path, (option["relativePaths"] as? Bool) ?? true) } - - let processIncludeOption = { (option: Any) -> (String, Bool)? in - if let option = option as? String { - return (option, true) - } else if let option = option as? JSONDictionary, let path = option["path"] as? String { - return (path, (option["relativePaths"] as? Bool) ?? true) - } - return nil - } - - let includeSources: [(String, Bool)] - if let sources = json["include"] as? [Any] { - includeSources = sources.compactMap { processIncludeOption($0) } - } else if let source = json["include"] { - includeSources = [processIncludeOption(source)].compactMap { $0 } - } else { - includeSources = [] - } - - let includes = try includeSources.map { include -> Spec in - let path = Path(include.0) - let basePath = include.1 ? basePath + relativePath : basePath + relativePath + path.parent() - let relativePath = include.1 ? path.parent() : Path() - - return try Spec(filename: path.lastComponent, basePath: basePath, relativePath: relativePath) - } - - self.relativePath = relativePath - self.jsonDictionary = json - self.subSpecs = includes + return nil } - public func resolvedDictionary() -> JSONDictionary { - return jsonDictionary.merged(onto: - subSpecs - .map { $0.resolvedDictionary() } - .reduce([:]) { $1.merged(onto: $0) } - ) + let includeSources: [(String, Bool)] + if let sources = json["include"] as? [Any] { + includeSources = sources.compactMap { processIncludeOption($0) } + } else if let source = json["include"] { + includeSources = [processIncludeOption(source)].compactMap { $0 } + } else { + includeSources = [] } + + let includes = try includeSources.map { include -> Spec in + let path = Path(include.0) + let basePath = include.1 ? basePath + relativePath : basePath + relativePath + path.parent() + let relativePath = include.1 ? path.parent() : Path() + + return try Spec(filename: path.lastComponent, basePath: basePath, relativePath: relativePath) + } + + self.relativePath = relativePath + self.jsonDictionary = json + self.subSpecs = includes + } + + public func resolvedDictionary() -> JSONDictionary { + return jsonDictionary.merged(onto: + subSpecs + .map { $0.resolvedDictionary() } + .reduce([:]) { $1.merged(onto: $0) } + ) } } -extension Project.Spec { - func resolvingPaths(relativeTo basePath: Path = Path()) -> Project.Spec { +extension Spec { + func resolvingPaths(relativeTo basePath: Path = Path()) -> Spec { let relativePath = (basePath + self.relativePath).normalize() guard relativePath != Path() else { return self @@ -81,7 +78,7 @@ extension Project.Spec { let jsonDictionary = Project.pathProperties.resolvingPaths(in: self.jsonDictionary, relativeTo: relativePath) - return Project.Spec( + return Spec( relativePath: self.relativePath, jsonDictionary: jsonDictionary, subSpecs: self.subSpecs.map { template in diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 4c0925e4..d8fbd374 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -12,7 +12,7 @@ extension Project { } public static func loadDictionary(path: Path) throws -> JSONDictionary { - return try Project.Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary + return try Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary } } diff --git a/Sources/XcodeGenKit/SpecLoader.swift b/Sources/XcodeGenKit/SpecLoader.swift index 2bc8eb77..86edd32d 100644 --- a/Sources/XcodeGenKit/SpecLoader.swift +++ b/Sources/XcodeGenKit/SpecLoader.swift @@ -16,7 +16,7 @@ public class SpecLoader { } public func loadProject(path: Path) throws -> Project { - let template = try Project.Spec(filename: path.lastComponent, basePath: path.parent()) + let template = try Spec(filename: path.lastComponent, basePath: path.parent()) let project = try Project(spec: template, basePath: path.parent()) self.project = project diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 63479905..3401adab 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -655,7 +655,7 @@ fileprivate func getProjectSpec(_ project: [String: Any], file: String = #file, projectDictionary[key] = value } do { - let template = Project.Spec(relativePath: "", jsonDictionary: projectDictionary) + let template = Spec(relativePath: "", jsonDictionary: projectDictionary) return try Project(spec: template, basePath: "") } catch { throw failure("\(error)", file: file, line: line) From 720b2eede1230d5ccd4e82bb9911321229a501cc Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 15:24:22 +0000 Subject: [PATCH 26/29] Move [PathProperty] resolver to spec loader file --- Sources/ProjectSpec/Spec.swift | 35 ---------------------------- Sources/ProjectSpec/SpecLoader.swift | 35 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Sources/ProjectSpec/Spec.swift b/Sources/ProjectSpec/Spec.swift index abff04d8..8852558a 100644 --- a/Sources/ProjectSpec/Spec.swift +++ b/Sources/ProjectSpec/Spec.swift @@ -88,41 +88,6 @@ extension Spec { } } -extension Array where Element == PathProperty { - func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - var result = jsonDictionary - - for pathProperty in self { - switch pathProperty { - case .string(let key): - if let source = result[key] as? String { - result[key] = (path + source).string - } else if let source = result[key] as? [String] { - result[key] = source.map { (path + $0).string } - } else if let source = result[key] as? [String: String] { - result[key] = source.mapValues { (path + $0).string } - } - case .dictionary(let pathProperties): - for (key, dictionary) in result { - if let source = dictionary as? JSONDictionary { - result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) - } - } - case .object(let key, let pathProperties): - if let source = result[key] as? JSONDictionary { - result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) - } else if let source = result[key] as? [JSONDictionary] { - result[key] = source.map { pathProperties.resolvingPaths(in: $0, relativeTo: path) } - } else if let source = result[key] as? [String: JSONDictionary] { - result[key] = source.mapValues { pathProperties.resolvingPaths(in: $0, relativeTo: path) } - } - } - } - - return result - } -} - extension Dictionary where Key == String, Value: Any { func merged(onto other: Dictionary) -> Dictionary { diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index d8fbd374..06f2523b 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -26,3 +26,38 @@ enum PathProperty { case dictionary([PathProperty]) case object(String, [PathProperty]) } + +extension Array where Element == PathProperty { + func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = jsonDictionary + + for pathProperty in self { + switch pathProperty { + case .string(let key): + if let source = result[key] as? String { + result[key] = (path + source).string + } else if let source = result[key] as? [String] { + result[key] = source.map { (path + $0).string } + } else if let source = result[key] as? [String: String] { + result[key] = source.mapValues { (path + $0).string } + } + case .dictionary(let pathProperties): + for (key, dictionary) in result { + if let source = dictionary as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } + } + case .object(let key, let pathProperties): + if let source = result[key] as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } else if let source = result[key] as? [JSONDictionary] { + result[key] = source.map { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } else if let source = result[key] as? [String: JSONDictionary] { + result[key] = source.mapValues { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } + } + } + + return result + } +} From 370963ec7303fbf36a62b73d9356b661d36139e2 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 15:26:58 +0000 Subject: [PATCH 27/29] Move PathContainer & PathProperty into their own file --- Sources/ProjectSpec/PathContainer.swift | 49 +++++++++++++++++++++++++ Sources/ProjectSpec/SpecLoader.swift | 47 ------------------------ 2 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 Sources/ProjectSpec/PathContainer.swift diff --git a/Sources/ProjectSpec/PathContainer.swift b/Sources/ProjectSpec/PathContainer.swift new file mode 100644 index 00000000..0179dc59 --- /dev/null +++ b/Sources/ProjectSpec/PathContainer.swift @@ -0,0 +1,49 @@ +import Foundation +import JSONUtilities +import PathKit + +protocol PathContainer { + + static var pathProperties: [PathProperty] { get } +} + +enum PathProperty { + case string(String) + case dictionary([PathProperty]) + case object(String, [PathProperty]) +} + +extension Array where Element == PathProperty { + func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { + var result = jsonDictionary + + for pathProperty in self { + switch pathProperty { + case .string(let key): + if let source = result[key] as? String { + result[key] = (path + source).string + } else if let source = result[key] as? [String] { + result[key] = source.map { (path + $0).string } + } else if let source = result[key] as? [String: String] { + result[key] = source.mapValues { (path + $0).string } + } + case .dictionary(let pathProperties): + for (key, dictionary) in result { + if let source = dictionary as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } + } + case .object(let key, let pathProperties): + if let source = result[key] as? JSONDictionary { + result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) + } else if let source = result[key] as? [JSONDictionary] { + result[key] = source.map { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } else if let source = result[key] as? [String: JSONDictionary] { + result[key] = source.mapValues { pathProperties.resolvingPaths(in: $0, relativeTo: path) } + } + } + } + + return result + } +} diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 06f2523b..db5ca726 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -1,7 +1,6 @@ import Foundation import JSONUtilities import PathKit -import Yams extension Project { @@ -15,49 +14,3 @@ extension Project { return try Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary } } - -protocol PathContainer { - - static var pathProperties: [PathProperty] { get } -} - -enum PathProperty { - case string(String) - case dictionary([PathProperty]) - case object(String, [PathProperty]) -} - -extension Array where Element == PathProperty { - func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { - var result = jsonDictionary - - for pathProperty in self { - switch pathProperty { - case .string(let key): - if let source = result[key] as? String { - result[key] = (path + source).string - } else if let source = result[key] as? [String] { - result[key] = source.map { (path + $0).string } - } else if let source = result[key] as? [String: String] { - result[key] = source.mapValues { (path + $0).string } - } - case .dictionary(let pathProperties): - for (key, dictionary) in result { - if let source = dictionary as? JSONDictionary { - result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) - } - } - case .object(let key, let pathProperties): - if let source = result[key] as? JSONDictionary { - result[key] = pathProperties.resolvingPaths(in: source, relativeTo: path) - } else if let source = result[key] as? [JSONDictionary] { - result[key] = source.map { pathProperties.resolvingPaths(in: $0, relativeTo: path) } - } else if let source = result[key] as? [String: JSONDictionary] { - result[key] = source.mapValues { pathProperties.resolvingPaths(in: $0, relativeTo: path) } - } - } - } - - return result - } -} From d54bbe711c89800eb01d3157edfc432adfa4d86e Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 15:27:30 +0000 Subject: [PATCH 28/29] #whitespace --- Sources/ProjectSpec/PathContainer.swift | 1 + Sources/ProjectSpec/Spec.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Sources/ProjectSpec/PathContainer.swift b/Sources/ProjectSpec/PathContainer.swift index 0179dc59..a21e1e2e 100644 --- a/Sources/ProjectSpec/PathContainer.swift +++ b/Sources/ProjectSpec/PathContainer.swift @@ -14,6 +14,7 @@ enum PathProperty { } extension Array where Element == PathProperty { + func resolvingPaths(in jsonDictionary: JSONDictionary, relativeTo path: Path) -> JSONDictionary { var result = jsonDictionary diff --git a/Sources/ProjectSpec/Spec.swift b/Sources/ProjectSpec/Spec.swift index 8852558a..10f2c415 100644 --- a/Sources/ProjectSpec/Spec.swift +++ b/Sources/ProjectSpec/Spec.swift @@ -70,6 +70,7 @@ public struct Spec { } extension Spec { + func resolvingPaths(relativeTo basePath: Path = Path()) -> Spec { let relativePath = (basePath + self.relativePath).normalize() guard relativePath != Path() else { From 8eadc2f96c1292129b80941ee0a4af54eac772a1 Mon Sep 17 00:00:00 2001 From: Ell Neal Date: Sun, 27 Jan 2019 15:34:41 +0000 Subject: [PATCH 29/29] Remove some imports that are no longer necessary --- Sources/ProjectSpec/AggregateTarget.swift | 1 - Sources/ProjectSpec/BuildScript.swift | 1 - Sources/ProjectSpec/Dependency.swift | 1 - Sources/ProjectSpec/Plist.swift | 1 - Sources/ProjectSpec/SpecOptions.swift | 1 - Sources/ProjectSpec/Target.swift | 1 - 6 files changed, 6 deletions(-) diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index 06c3beb9..e25fb553 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit public struct AggregateTarget: ProjectTarget { public var name: String diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index ebb86cd1..7042e18a 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit public struct BuildScript: Equatable { diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 1a214258..881eeee5 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit public struct Dependency: Equatable { diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index c3a31bbf..1f295106 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit public struct Plist: Equatable { diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index 591fd2a3..9bfb5a2d 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit public struct SpecOptions: Equatable { diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index f5cacaab..0a9b748f 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -1,6 +1,5 @@ import Foundation import JSONUtilities -import PathKit import xcodeproj public struct LegacyTarget: Equatable {