Skip to content

Commit

Permalink
Merge pull request #205 from yonaskolb/platform_version
Browse files Browse the repository at this point in the history
Add deployment target
  • Loading branch information
yonaskolb authored Dec 26, 2017
2 parents d4fc091 + e3a6739 commit 554a9d2
Show file tree
Hide file tree
Showing 18 changed files with 278 additions and 57 deletions.
11 changes: 10 additions & 1 deletion Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ Note that target names can also be changed by adding a `name` property to a targ
- [ ] **indentWidth**: **Int** - If this is specified, the Xcode project will override the user's setting for indent width in number of spaces.
- [ ] **tabWidth**: **Int** - If this is specified, the Xcode project will override the user's setting for indent width in number of spaces.
- [ ] **xcodeVersion**: **String** - The version of Xcode. This defaults to the latest version periodically. You can specify it in the format `0910` or `9.1`
- [ ] **deploymentTarget**: **[[Platform](#platform): String]** - A project wide deployment target can be specified for each platform otherwise the default SDK version in Xcode will be used. This will be overridden by any custom build settings that set the deployment target eg `IPHONEOS_DEPLOYMENT_TARGET`. Target specific deployment targets can also be set with [Target](#target).deploymentTarget.

```yaml
options:
deploymentTarget:
watchOS: 2.0
tvOS: 10.0
```

### Configs
Each config maps to a build type of either `debug` or `release` which will then apply default build settings to the project. Any value other than `debug` or `release` (for example `none`), will mean no default build settings will be applied to the project.
Expand Down Expand Up @@ -137,6 +145,7 @@ Settings are merged in the following order: groups, base, configs.

- [x] **type**: **[Product Type](#product-type)** - Product type of the target
- [x] **platform**: **[Platform](#platform)** - Platform of the target
- [ ] **deploymentTarget**: **String or Double** - The deployment target eg (11.0). If this is not specified the value from the project set in [Options](#options)`.deploymentTarget.PLATFORM` will be used.
- [ ] **sources**: **[Sources](#sources)** - Source directories of the target
- [ ] **configFiles**: **[Config Files](#config-files)** - `.xcconfig` files per config
- [ ] **settings**: **[Settings](#settings)** - Target specific build settings. Default platform and product type settings will be applied first before any custom settings defined here. Other context dependant settings will be set automatically as well:
Expand Down Expand Up @@ -356,7 +365,7 @@ For example, the spec below would create 3 schemes called:
Each scheme would use different build configuration for the different build types, specifically debug configs for `run`, `test`, and `analyze`, and release configs for `profile` and `archive`.
The MyUnitTests target would also be linked.

```
```yaml
configs:
Test Debug: debug
Staging Debug: debug
Expand Down
1 change: 0 additions & 1 deletion SettingPresets/Platforms/iOS.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: iphoneos
IPHONEOS_DEPLOYMENT_TARGET: 10.0
TARGETED_DEVICE_FAMILY: '1,2'
1 change: 0 additions & 1 deletion SettingPresets/Platforms/macOS.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/../Frameworks"
SDKROOT: macosx
MACOSX_DEPLOYMENT_TARGET: 10.12
COMBINE_HIDPI_IMAGES: 'YES'
1 change: 0 additions & 1 deletion SettingPresets/Platforms/tvOS.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
TARGETED_DEVICE_FAMILY: 3
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: appletvos
TVOS_DEPLOYMENT_TARGET: "10.0"
1 change: 0 additions & 1 deletion SettingPresets/Platforms/watchOS.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
SDKROOT: watchos
SKIP_INSTALL: 'YES'
TARGETED_DEVICE_FAMILY: 4
WATCHOS_DEPLOYMENT_TARGET: 3.0
82 changes: 82 additions & 0 deletions Sources/ProjectSpec/DeploymentTarget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// Version.swift
// ProjectSpec
//
// Created by Yonas Kolb on 22/12/17.
//

import Foundation
import xcproj
import JSONUtilities

public struct DeploymentTarget: Equatable {

public var iOS: Version?
public var tvOS: Version?
public var watchOS: Version?
public var macOS: Version?

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) -> Version? {
switch platform {
case .iOS: return iOS
case .tvOS: return tvOS
case .watchOS: return watchOS
case .macOS: return macOS
}
}

public static func == (lhs: DeploymentTarget, rhs: DeploymentTarget) -> Bool {
return lhs.iOS == rhs.iOS &&
lhs.tvOS == rhs.tvOS &&
lhs.watchOS == rhs.watchOS &&
lhs.macOS == rhs.macOS
}
}

extension Platform {

public var deploymentTargetSetting: String {
switch self {
case .iOS: return "IPHONEOS_DEPLOYMENT_TARGET"
case .tvOS: return "TVOS_DEPLOYMENT_TARGET"
case .watchOS: return "WATCHOS_DEPLOYMENT_TARGET"
case .macOS: return "MACOSX_DEPLOYMENT_TARGET"
}
}
}

extension Version {

/// doesn't print patch if 0
public var deploymentTarget: String {
return "\(major).\(minor)\(patch > 0 ? ".\(patch)" : "")"
}
}

extension DeploymentTarget: JSONObjectConvertible {

public init(jsonDictionary: JSONDictionary) throws {

func parseVersion(_ platform: String) throws -> Version? {
if let string: String = jsonDictionary.json(atKeyPath: .key(platform)) {
return try Version(string)
} else if let double: Double = jsonDictionary.json(atKeyPath: .key(platform)) {
return try Version(double)
} else {
return nil
}
}
iOS = try parseVersion("iOS")
tvOS = try parseVersion("tvOS")
watchOS = try parseVersion("watchOS")
macOS = try parseVersion("macOS")
}

}
2 changes: 2 additions & 0 deletions Sources/ProjectSpec/Platform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public enum Platform: String {
return rawValue
}
}

public static var all: [Platform] = [.iOS, .tvOS, .watchOS, .macOS]
}
8 changes: 6 additions & 2 deletions Sources/ProjectSpec/ProjectSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public struct ProjectSpec {
public var tabWidth: Int?
public var indentWidth: Int?
public var xcodeVersion: String?
public var deploymentTarget: DeploymentTarget

public enum SettingPresets: String {
case all
Expand All @@ -51,7 +52,7 @@ public struct ProjectSpec {
}
}

public init(carthageBuildPath: String? = nil, createIntermediateGroups: Bool = false, bundleIdPrefix: String? = nil, settingPresets: SettingPresets = .all, developmentLanguage: String? = nil, indentWidth: Int? = nil, tabWidth: Int? = nil, usesTabs: Bool? = nil, xcodeVersion: String? = nil) {
public init(carthageBuildPath: String? = nil, createIntermediateGroups: Bool = false, bundleIdPrefix: String? = nil, settingPresets: SettingPresets = .all, developmentLanguage: String? = nil, indentWidth: Int? = nil, tabWidth: Int? = nil, usesTabs: Bool? = nil, xcodeVersion: String? = nil, deploymentTarget: DeploymentTarget = .init()) {
self.carthageBuildPath = carthageBuildPath
self.createIntermediateGroups = createIntermediateGroups
self.bundleIdPrefix = bundleIdPrefix
Expand All @@ -61,6 +62,7 @@ public struct ProjectSpec {
self.indentWidth = indentWidth
self.usesTabs = usesTabs
self.xcodeVersion = xcodeVersion
self.deploymentTarget = deploymentTarget
}

public static func == (lhs: ProjectSpec.Options, rhs: ProjectSpec.Options) -> Bool {
Expand All @@ -72,7 +74,8 @@ public struct ProjectSpec {
lhs.tabWidth == rhs.tabWidth &&
lhs.indentWidth == rhs.indentWidth &&
lhs.usesTabs == rhs.usesTabs &&
lhs.xcodeVersion == rhs.xcodeVersion
lhs.xcodeVersion == rhs.xcodeVersion &&
lhs.deploymentTarget == rhs.deploymentTarget
}
}

Expand Down Expand Up @@ -175,5 +178,6 @@ extension ProjectSpec.Options: JSONObjectConvertible {
usesTabs = jsonDictionary.json(atKeyPath: "usesTabs")
indentWidth = jsonDictionary.json(atKeyPath: "indentWidth")
tabWidth = jsonDictionary.json(atKeyPath: "tabWidth")
deploymentTarget = jsonDictionary.json(atKeyPath: "deploymentTarget") ?? DeploymentTarget()
}
}
2 changes: 2 additions & 0 deletions Sources/ProjectSpec/SpecParsingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ public enum SpecParsingError: Error, CustomStringConvertible {
case unknownTargetPlatform(String)
case invalidDependency([String: Any])
case unknownSourceBuildPhase(String)
case invalidVersion(String)

public var description: String {
switch self {
case let .unknownTargetType(type): return "Unknown Target type: \(type)"
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)"
}
}
}
14 changes: 13 additions & 1 deletion Sources/ProjectSpec/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public struct Target {
public var configFiles: [String: String]
public var scheme: TargetScheme?
public var legacy: LegacyTarget?
public var deploymentTarget: Version?

public var isLegacy: Bool {
return legacy != nil
Expand All @@ -43,10 +44,11 @@ public struct Target {
return name
}

public init(name: String, type: PBXProductType, platform: Platform, 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, deploymentTarget: 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
self.deploymentTarget = deploymentTarget
self.settings = settings
self.configFiles = configFiles
self.sources = sources
Expand Down Expand Up @@ -138,6 +140,7 @@ extension Target: Equatable {
return lhs.name == rhs.name &&
lhs.type == rhs.type &&
lhs.platform == rhs.platform &&
lhs.deploymentTarget == rhs.deploymentTarget &&
lhs.settings == rhs.settings &&
lhs.configFiles == rhs.configFiles &&
lhs.sources == rhs.sources &&
Expand Down Expand Up @@ -207,6 +210,15 @@ extension Target: NamedJSONDictionaryConvertible {
} else {
throw SpecParsingError.unknownTargetPlatform(platformString)
}

if let string: String = jsonDictionary.json(atKeyPath: "deploymentTarget") {
deploymentTarget = try Version(string)
} else if let double: Double = jsonDictionary.json(atKeyPath: "deploymentTarget") {
deploymentTarget = try Version(double)
} else {
deploymentTarget = nil
}

settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
if let source: String = jsonDictionary.json(atKeyPath: "sources") {
Expand Down
56 changes: 56 additions & 0 deletions Sources/ProjectSpec/Version.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
12 changes: 12 additions & 0 deletions Sources/XcodeGenKit/SettingsBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ extension ProjectSpec {
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
}

// apply custom platform version
for platform in Platform.all {
if let version = options.deploymentTarget.version(for: platform) {
buildSettings[platform.deploymentTargetSetting] = version.deploymentTarget
}
}

// Prevent setting presets from overrwriting settings in project xcconfig files
if let configPath = configFiles[config.name] {
buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath)
Expand All @@ -34,6 +41,11 @@ extension ProjectSpec {
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
}

// apply custom platform version
if let version = target.deploymentTarget {
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
}

// Prevent setting presets from overrwriting settings in target xcconfig files
if let configPath = target.configFiles[config.name] {
buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath)
Expand Down
Loading

0 comments on commit 554a9d2

Please sign in to comment.