From 4a90d33951bb3048342ce8ea30e55a9762738dd4 Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Tue, 26 Dec 2017 17:36:41 +0800 Subject: [PATCH] Add Version type --- Sources/ProjectSpec/PlatformVersions.swift | 34 ++++++----- Sources/ProjectSpec/SpecParsingError.swift | 2 + Sources/ProjectSpec/Target.swift | 8 +-- Sources/ProjectSpec/Version.swift | 56 +++++++++++++++++++ Sources/XcodeGenKit/SettingsBuilder.swift | 4 +- Tests/XcodeGenKitTests/ProjectSpecTests.swift | 45 +++++++++++---- Tests/XcodeGenKitTests/SpecLoadingTests.swift | 8 ++- Tests/XcodeGenKitTests/TestHelpers.swift | 9 +++ 8 files changed, 134 insertions(+), 32 deletions(-) create mode 100644 Sources/ProjectSpec/Version.swift diff --git a/Sources/ProjectSpec/PlatformVersions.swift b/Sources/ProjectSpec/PlatformVersions.swift index 6ad2c00b..75f693a2 100644 --- a/Sources/ProjectSpec/PlatformVersions.swift +++ b/Sources/ProjectSpec/PlatformVersions.swift @@ -11,19 +11,19 @@ import JSONUtilities public struct PlatformVersions: Equatable { - public var iOS: String? - public var tvOS: String? - public var watchOS: String? - public var macOS: String? + public var iOS: Version? + public var tvOS: Version? + public var watchOS: Version? + public var macOS: Version? - public init(iOS: String? = nil, tvOS: String? = nil, watchOS: String? = nil, macOS: String? = nil) { + public init(iOS: Version? = nil, tvOS: Version? = nil, watchOS: Version? = nil, macOS: Version? = nil) { self.iOS = iOS self.tvOS = tvOS self.watchOS = watchOS self.macOS = macOS } - public func version(for platform: Platform) -> String? { + public func version(for platform: Platform) -> Version? { switch platform { case .iOS: return iOS case .tvOS: return tvOS @@ -52,23 +52,31 @@ extension Platform { } } +extension Version { + + /// doesn't print patch + public var platformVersion: String { + return "\(major).\(minor)\(patch > 0 ? ".\(patch)" : "")" + } +} + extension PlatformVersions: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { - func parseVersion(_ platform: String) -> String? { + func parseVersion(_ platform: String) throws -> Version? { if let string: String = jsonDictionary.json(atKeyPath: .key(platform)) { - return string + return try Version(string) } else if let double: Double = jsonDictionary.json(atKeyPath: .key(platform)) { - return String(double) + return try Version(double) } else { return nil } } - iOS = parseVersion("iOS") - tvOS = parseVersion("tvOS") - watchOS = parseVersion("watchOS") - macOS = parseVersion("macOS") + iOS = try parseVersion("iOS") + tvOS = try parseVersion("tvOS") + watchOS = try parseVersion("watchOS") + macOS = try parseVersion("macOS") } } diff --git a/Sources/ProjectSpec/SpecParsingError.swift b/Sources/ProjectSpec/SpecParsingError.swift index 5183eb5e..b6dae490 100644 --- a/Sources/ProjectSpec/SpecParsingError.swift +++ b/Sources/ProjectSpec/SpecParsingError.swift @@ -5,6 +5,7 @@ public enum SpecParsingError: Error, CustomStringConvertible { case unknownTargetPlatform(String) case invalidDependency([String: Any]) case unknownSourceBuildPhase(String) + case invalidVersion(String) public var description: String { switch self { @@ -12,6 +13,7 @@ public enum SpecParsingError: Error, CustomStringConvertible { case let .unknownTargetPlatform(platform): return "Unknown Target platform: \(platform)" case let .invalidDependency(dependency): return "Unknown Target dependency: \(dependency)" case let .unknownSourceBuildPhase(buildPhase): return "Unknown Source Build Phase: \(buildPhase)" + case let .invalidVersion(version): return "Invalid version: \(version)" } } } diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index 393dbf6e..96eef365 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -30,7 +30,7 @@ public struct Target { public var configFiles: [String: String] public var scheme: TargetScheme? public var legacy: LegacyTarget? - public var platformVersion: String? + public var platformVersion: Version? public var isLegacy: Bool { return legacy != nil @@ -44,7 +44,7 @@ public struct Target { return name } - public init(name: String, type: PBXProductType, platform: Platform, platformVersion: String? = nil, settings: Settings = .empty, configFiles: [String: String] = [:], sources: [TargetSource] = [], dependencies: [Dependency] = [], prebuildScripts: [BuildScript] = [], postbuildScripts: [BuildScript] = [], scheme: TargetScheme? = nil, legacy: LegacyTarget? = nil) { + public init(name: String, type: PBXProductType, platform: Platform, platformVersion: Version? = nil, settings: Settings = .empty, configFiles: [String: String] = [:], sources: [TargetSource] = [], dependencies: [Dependency] = [], prebuildScripts: [BuildScript] = [], postbuildScripts: [BuildScript] = [], scheme: TargetScheme? = nil, legacy: LegacyTarget? = nil) { self.name = name self.type = type self.platform = platform @@ -212,9 +212,9 @@ extension Target: NamedJSONDictionaryConvertible { } if let string: String = jsonDictionary.json(atKeyPath: "platformVersion") { - platformVersion = string + platformVersion = try Version(string) } else if let double: Double = jsonDictionary.json(atKeyPath: "platformVersion") { - platformVersion = String(double) + platformVersion = try Version(double) } else { platformVersion = nil } diff --git a/Sources/ProjectSpec/Version.swift b/Sources/ProjectSpec/Version.swift new file mode 100644 index 00000000..263201d6 --- /dev/null +++ b/Sources/ProjectSpec/Version.swift @@ -0,0 +1,56 @@ +import Foundation + +public struct Version: CustomStringConvertible, Equatable { + + public var major: UInt + public var minor: UInt + public var patch: UInt + + public init(_ string: String) throws { + let components = try string.split(separator: ".").map { (componentString) -> UInt in + guard let uint = UInt(componentString) else { + throw SpecParsingError.invalidVersion(string) + } + return uint + } + major = components[0] + minor = (components.count >= 2) ? components[1] : 0 + patch = (components.count == 3) ? components[2] : 0 + } + + public init(_ double: Double) throws { + try self.init(String(double)) + } + + public init(major: UInt, minor: UInt? = 0, patch: UInt? = 0) { + self.major = major + self.minor = minor ?? 0 + self.patch = patch ?? 0 + } + + public var string: String { + return "\(major).\(minor).\(patch)" + } + + public var description: String { + return string + } + + public func bumpingMajor() -> Version { + return Version(major: major + 1, minor: 0, patch: 0) + } + + public func bumpingMinor() -> Version { + return Version(major: major, minor: minor + 1, patch: 0) + } + + public func bumpingPatch() -> Version { + return Version(major: major, minor: minor, patch: patch + 1) + } + + public static func ==(lhs: Version, rhs: Version) -> Bool { + return lhs.major == rhs.major && + lhs.minor == rhs.minor && + lhs.patch == rhs.patch + } +} diff --git a/Sources/XcodeGenKit/SettingsBuilder.swift b/Sources/XcodeGenKit/SettingsBuilder.swift index 1c8e0d3d..b6487468 100644 --- a/Sources/XcodeGenKit/SettingsBuilder.swift +++ b/Sources/XcodeGenKit/SettingsBuilder.swift @@ -18,7 +18,7 @@ extension ProjectSpec { // apply custom platform version for platform in Platform.all { if let version = options.platformVersions.version(for: platform) { - buildSettings[platform.versionBuildSetting] = version + buildSettings[platform.versionBuildSetting] = version.platformVersion } } @@ -43,7 +43,7 @@ extension ProjectSpec { // apply custom platform version if let version = target.platformVersion { - buildSettings[target.platform.versionBuildSetting] = version + buildSettings[target.platform.versionBuildSetting] = version.platformVersion } // Prevent setting presets from overrwriting settings in target xcconfig files diff --git a/Tests/XcodeGenKitTests/ProjectSpecTests.swift b/Tests/XcodeGenKitTests/ProjectSpecTests.swift index 2bc7ff06..bfddd0e2 100644 --- a/Tests/XcodeGenKitTests/ProjectSpecTests.swift +++ b/Tests/XcodeGenKitTests/ProjectSpecTests.swift @@ -14,17 +14,6 @@ func projectSpecTests() { let dynamicLibrary = Target(name: "MyDynamicLibrary", type: .dynamicLibrary, platform: .iOS, settings: Settings(buildSettings: ["SETTING_2": "VALUE"])) - $0.describe("Types") { - $0.it("is a framework when it has the right extension") { - try expect(framework.type.isFramework).to.beTrue() - } - - $0.it("is a library when it has the right type") { - try expect(staticLibrary.type.isLibrary).to.beTrue() - try expect(dynamicLibrary.type.isLibrary).to.beTrue() - } - } - func expectValidationError(_ spec: ProjectSpec, _ expectedError: SpecValidationError.ValidationError) throws { do { try spec.validate() @@ -39,6 +28,40 @@ func projectSpecTests() { throw failure("Supposed to fail with \"\(expectedError)\"") } + $0.describe("Types") { + $0.it("is a framework when it has the right extension") { + try expect(framework.type.isFramework).to.beTrue() + } + + $0.it("is a library when it has the right type") { + try expect(staticLibrary.type.isLibrary).to.beTrue() + try expect(dynamicLibrary.type.isLibrary).to.beTrue() + } + } + + $0.describe("Deployment Version") { + + $0.it("has correct build setting") { + try expect(Platform.iOS.versionBuildSetting) == "IPHONEOS_DEPLOYMENT_TARGET" + try expect(Platform.tvOS.versionBuildSetting) == "TVOS_DEPLOYMENT_TARGET" + try expect(Platform.watchOS.versionBuildSetting) == "WATCHOS_DEPLOYMENT_TARGET" + try expect(Platform.macOS.versionBuildSetting) == "MACOSX_DEPLOYMENT_TARGET" + } + + $0.it("parses versions correctly") { + try expect(Version("2").platformVersion) == "2.0" + try expect(Version("2.0").platformVersion) == "2.0" + try expect(Version("2.1").platformVersion) == "2.1" + try expect(Version("2.1.0").platformVersion) == "2.1" + try expect(Version("2.12.0").platformVersion) == "2.12" + try expect(Version("2.1.2").platformVersion) == "2.1.2" + try expect(Version("2.0.2").platformVersion) == "2.0.2" + try expect(Version(2).platformVersion) == "2.0" + try expect(Version(2.0).platformVersion) == "2.0" + try expect(Version(2.1).platformVersion) == "2.1" + } + } + $0.describe("Validation") { let baseSpec = ProjectSpec(basePath: "", name: "", configs: [Config(name: "invalid")]) diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 30dfdc88..af4934a3 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -230,14 +230,18 @@ func specLoadingTests() { createIntermediateGroups: true, bundleIdPrefix: "com.test", developmentLanguage: "ja", - platformVersions: PlatformVersions(iOS: "11.1", tvOS: "10.0", watchOS: "3.0", macOS: "10.12")) + platformVersions: PlatformVersions( + iOS: "11.1", + tvOS: "10.0", + watchOS: "3.0", + macOS: "10.12.1")) let expected = ProjectSpec(basePath: "", name: "test", options: options) let dictionary: [String: Any] = ["options": [ "carthageBuildPath": "../Carthage/Build", "bundleIdPrefix": "com.test", "createIntermediateGroups": true, "developmentLanguage": "ja", - "platformVersions": ["iOS": 11.1, "tvOS": 10.0, "watchOS": "3.0", "macOS": "10.12" ] + "platformVersions": ["iOS": 11.1, "tvOS": 10.0, "watchOS": "3", "macOS": "10.12.1" ] ]] let parsedSpec = try getProjectSpec(dictionary) try expect(parsedSpec) == expected diff --git a/Tests/XcodeGenKitTests/TestHelpers.swift b/Tests/XcodeGenKitTests/TestHelpers.swift index 84eb5104..03065030 100644 --- a/Tests/XcodeGenKitTests/TestHelpers.swift +++ b/Tests/XcodeGenKitTests/TestHelpers.swift @@ -1,6 +1,7 @@ import Foundation import Spectre import xcproj +import ProjectSpec func expectError(_ expectedError: T, _ closure: () throws -> Void) throws where T: CustomStringConvertible { do { @@ -88,3 +89,11 @@ public protocol Named { extension XCBuildConfiguration: Named {} extension PBXNativeTarget: Named {} extension XCScheme: Named {} + +extension Version: ExpressibleByStringLiteral { + + /// Will return nil literal not Semver + public init(stringLiteral value: String) { + try! self.init(value) + } +}