From fa5bd83049df7ac7a498af3f545311b51cb4bd20 Mon Sep 17 00:00:00 2001 From: yonaskolb Date: Fri, 27 Sep 2019 17:10:55 +1000 Subject: [PATCH] add packages to project spec --- Sources/ProjectSpec/Dependency.swift | 7 ++ Sources/ProjectSpec/Project.swift | 40 ++++++++---- Sources/ProjectSpec/SpecParsingError.swift | 3 + Sources/ProjectSpec/SwiftPackage.swift | 75 ++++++++++++++++++++++ 4 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 Sources/ProjectSpec/SwiftPackage.swift diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 022bcbcd..a31c0bd3 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -38,6 +38,7 @@ public struct Dependency: Equatable { case framework case carthage(findFrameworks: Bool?) case sdk(root: String?) + case package(product: String?) } } @@ -64,6 +65,10 @@ extension Dependency: JSONObjectConvertible { let sdkRoot: String? = jsonDictionary.json(atKeyPath: "root") type = .sdk(root: sdkRoot) reference = sdk + } else if let package: String = jsonDictionary.json(atKeyPath: "package") { + let product: String? = jsonDictionary.json(atKeyPath: "product") + type = .package(product: product) + reference = package } else { throw SpecParsingError.invalidDependency(jsonDictionary) } @@ -114,6 +119,8 @@ extension Dependency: JSONEncodable { } case .sdk: dict["sdk"] = reference + case .package: + dict["package"] = reference } return dict diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index adfdf029..95858840 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -19,6 +19,8 @@ public struct Project: BuildSettingsContainer { } } + public var packages: [String: SwiftPackage] + public var settings: Settings public var settingGroups: [String: Settings] public var configs: [Config] @@ -41,6 +43,7 @@ public struct Project: BuildSettingsContainer { settings: Settings = .empty, settingGroups: [String: Settings] = [:], schemes: [Scheme] = [], + packages: [String: SwiftPackage] = [:], options: SpecOptions = SpecOptions(), fileGroups: [String] = [], configFiles: [String: String] = [:], @@ -56,6 +59,7 @@ public struct Project: BuildSettingsContainer { self.settings = settings self.settingGroups = settingGroups self.schemes = schemes + self.packages = packages self.options = options self.fileGroups = fileGroups self.configFiles = configFiles @@ -126,6 +130,7 @@ extension Project: Equatable { lhs.fileGroups == rhs.fileGroups && lhs.configFiles == rhs.configFiles && lhs.options == rhs.options && + lhs.packages == rhs.packages && NSDictionary(dictionary: lhs.attributes).isEqual(to: rhs.attributes) } } @@ -160,6 +165,11 @@ extension Project { configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:] attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:] include = jsonDictionary.json(atKeyPath: "include") ?? [] + if jsonDictionary["packages"] != nil { + packages = try jsonDictionary.json(atKeyPath: "packages", invalidItemBehaviour: .fail) + } else { + packages = [:] + } if jsonDictionary["options"] != nil { options = try jsonDictionary.json(atKeyPath: "options") } else { @@ -242,19 +252,21 @@ extension Project: JSONEncodable { let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) } let schemesPairs = schemes.map { ($0.name, $0.toJSONValue()) } - return [ - "name": name, - "options": options.toJSONValue(), - "settings": settings.toJSONValue(), - "fileGroups": fileGroups, - "configFiles": configFiles, - "include": include, - "attributes": attributes, - "targets": Dictionary(uniqueKeysWithValues: targetPairs), - "configs": Dictionary(uniqueKeysWithValues: configsPairs), - "aggregateTargets": Dictionary(uniqueKeysWithValues: aggregateTargetsPairs), - "schemes": Dictionary(uniqueKeysWithValues: schemesPairs), - "settingGroups": settingGroups.mapValues { $0.toJSONValue() }, - ] + var dictionary: JSONDictionary = [:] + dictionary["name"] = name + dictionary["options"] = options.toJSONValue() + dictionary["settings"] = settings.toJSONValue() + dictionary["fileGroups"] = fileGroups + dictionary["configFiles"] = configFiles + dictionary["include"] = include + dictionary["attributes"] = attributes + dictionary["packages"] = packages.mapValues { $0.toJSONValue() } + dictionary["targets"] = Dictionary(uniqueKeysWithValues: targetPairs) + dictionary["configs"] = Dictionary(uniqueKeysWithValues: configsPairs) + dictionary["aggregateTargets"] = Dictionary(uniqueKeysWithValues: aggregateTargetsPairs) + dictionary["schemes"] = Dictionary(uniqueKeysWithValues: schemesPairs) + dictionary["settingGroups"] = settingGroups.mapValues { $0.toJSONValue() } + + return dictionary } } diff --git a/Sources/ProjectSpec/SpecParsingError.swift b/Sources/ProjectSpec/SpecParsingError.swift index 5d48d4d0..6afa5bf2 100644 --- a/Sources/ProjectSpec/SpecParsingError.swift +++ b/Sources/ProjectSpec/SpecParsingError.swift @@ -4,6 +4,7 @@ public enum SpecParsingError: Error, CustomStringConvertible { case unknownTargetType(String) case unknownTargetPlatform(String) case invalidDependency([String: Any]) + case unknownPackageRequirement([String: Any]) case invalidSourceBuildPhase(String) case invalidVersion(String) @@ -19,6 +20,8 @@ public enum SpecParsingError: Error, CustomStringConvertible { return "Invalid Source Build Phase: \(error)" case let .invalidVersion(version): return "Invalid version: \(version)" + case let .unknownPackageRequirement(package): + return "Unknown package requirement: \(package)" } } } diff --git a/Sources/ProjectSpec/SwiftPackage.swift b/Sources/ProjectSpec/SwiftPackage.swift new file mode 100644 index 00000000..a0955456 --- /dev/null +++ b/Sources/ProjectSpec/SwiftPackage.swift @@ -0,0 +1,75 @@ +import Foundation +import XcodeProj +import JSONUtilities + +public struct SwiftPackage: Equatable { + + public typealias VersionRequirement = XCRemoteSwiftPackageReference.VersionRequirement + + public let url: String + public let versionRequirement: VersionRequirement + + public init(url: String, versionRequirement: VersionRequirement) { + self.url = url + self.versionRequirement = versionRequirement + } +} + +extension SwiftPackage: JSONObjectConvertible { + + public init(jsonDictionary: JSONDictionary) throws { + url = try jsonDictionary.json(atKeyPath: "url") + versionRequirement = try VersionRequirement(jsonDictionary: jsonDictionary) + } +} + +extension SwiftPackage: JSONEncodable { + + public func toJSONValue() -> Any { + var dictionary: JSONDictionary = [:] + dictionary["url"] = url + + switch versionRequirement { + + case .upToNextMajorVersion(let version): + dictionary["majorVersion"] = version + case .upToNextMinorVersion(let version): + dictionary["minorVersion"] = version + case .range(let from, let to): + dictionary["minVersion"] = from + dictionary["maxVersion"] = to + case .exact(let version): + dictionary["exactVersion"] = version + case .branch(let branch): + dictionary["branch"] = branch + case .revision(let revision): + dictionary["revision"] = revision + } + return dictionary + } +} + +extension SwiftPackage.VersionRequirement: JSONObjectConvertible { + + public init(jsonDictionary: JSONDictionary) throws { + if jsonDictionary["exactVersion"] != nil { + self = try .exact(jsonDictionary.json(atKeyPath: "exactVersion")) + } else if jsonDictionary["revision"] != nil { + self = try .revision(jsonDictionary.json(atKeyPath: "revision")) + } else if jsonDictionary["branch"] != nil { + self = try .branch(jsonDictionary.json(atKeyPath: "branch")) + } else if jsonDictionary["minVersion"] != nil && jsonDictionary["maxVersion"] != nil { + let minimum: String = try jsonDictionary.json(atKeyPath: "minVersion") + let maximum: String = try jsonDictionary.json(atKeyPath: "maxVersion") + self = .range(from: minimum, to: maximum) + } else if jsonDictionary["minorVersion"] != nil { + self = try .upToNextMinorVersion(jsonDictionary.json(atKeyPath: "minorVersion")) + } else if jsonDictionary["majorVersion"] != nil { + self = try .upToNextMajorVersion(jsonDictionary.json(atKeyPath: "majorVersion")) + } else if jsonDictionary["from"] != nil { + self = try .upToNextMajorVersion(jsonDictionary.json(atKeyPath: "from")) + } else { + throw SpecParsingError.unknownPackageRequirement(jsonDictionary) + } + } +}