Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add deployment target #205

Merged
merged 8 commits into from
Dec 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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