Skip to content

Commit

Permalink
Merge pull request #35 from yonaskolb/multi_platform_targets
Browse files Browse the repository at this point in the history
Generate multi platform targets
  • Loading branch information
yonaskolb authored Aug 26, 2017
2 parents 6f9f194 + da4b382 commit 9a93189
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 5 deletions.
6 changes: 1 addition & 5 deletions Sources/ProjectSpec/ProjectSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,7 @@ extension ProjectSpec {
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? [:]
let configs: [String: String] = jsonDictionary.json(atKeyPath: "configs") ?? [:]
self.configs = configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }
if jsonDictionary["targets"] == nil {
targets = []
} else {
targets = try jsonDictionary.json(atKeyPath: "targets", invalidItemBehaviour: .fail)
}
self.targets = try Target.decodeTargets(jsonDictionary: jsonDictionary)
schemes = try jsonDictionary.json(atKeyPath: "schemes")
if jsonDictionary["options"] != nil {
options = try jsonDictionary.json(atKeyPath: "options")
Expand Down
70 changes: 70 additions & 0 deletions Sources/ProjectSpec/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,76 @@ public struct Target {
}
}

extension Target {

static func decodeTargets(jsonDictionary: JSONDictionary) throws -> [Target] {
guard jsonDictionary["targets"] != nil else {
return []
}
let array: [JSONDictionary] = try jsonDictionary.json(atKeyPath: "targets", invalidItemBehaviour: .fail)

var targets: [JSONDictionary] = []

let platformReplacement = "$platform"

for json in array {

if let platforms = json["platform"] as? [String] {

for platform in platforms {
var platformTarget = json

func replacePlatform(_ dictionary: JSONDictionary) -> JSONDictionary {
var replaced = dictionary
for (key, value) in dictionary {
switch value {
case let dictionary as JSONDictionary:
replaced[key] = replacePlatform(dictionary)
case let string as String:
replaced[key] = string.replacingOccurrences(of: platformReplacement, with: platform)
case let array as [JSONDictionary]:
replaced[key] = array.map(replacePlatform)
case let array as [String]:
replaced[key] = array.map { $0.replacingOccurrences(of: platformReplacement, with: platform) }
default: break
}
}
return replaced
}

platformTarget = replacePlatform(platformTarget)

platformTarget["platform"] = platform
let platformSuffix = platformTarget["platformSuffix"] as? String ?? "_\(platform)"
let platformPrefix = platformTarget["platformPrefix"] as? String ?? ""
let name = platformTarget["name"] as? String ?? ""
platformTarget["name"] = platformPrefix + name + platformSuffix

var settings = platformTarget["settings"] as? JSONDictionary ?? [:]
if settings["configs"] != nil || settings["presets"] != nil || settings["base"] != nil {
var base = settings["base"] as? JSONDictionary ?? [:]
if base["PRODUCT_NAME"] == nil {
base["PRODUCT_NAME"] = name
}
settings["base"] = base
} else {
if settings["PRODUCT_NAME"] == nil {
settings["PRODUCT_NAME"] = name
}
}
platformTarget["settings"] = settings

targets.append(platformTarget)
}
} else {
targets.append(json)
}
}

return try targets.map { try Target(jsonDictionary: $0) }
}
}

extension Target: Equatable {

public static func ==(lhs: Target, rhs: Target) -> Bool {
Expand Down
23 changes: 23 additions & 0 deletions Tests/XcodeGenKitTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ func specLoadingTests() {
try expect(target.dependencies[2]) == .framework("path")
}

$0.it("parsed cross platform targets") {
let targetDictionary: [String: Any] = [
"name":"Framework",
"platform": ["iOS", "tvOS"],
"type": "framework",
"sources": ["Framework", "Framework $platform"],
"settings": ["SETTING": "value_$platform"],
]

let spec = try getProjectSpec(["targets":[targetDictionary]])
var target_iOS = Target(name: "Framework_iOS", type: .framework, platform: .iOS)
var target_tvOS = Target(name: "Framework_tvOS", type: .framework, platform: .tvOS)

target_iOS.sources = ["Framework","Framework iOS"]
target_tvOS.sources = ["Framework","Framework tvOS"]
target_iOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_iOS"]
target_tvOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_tvOS"]

try expect(spec.targets.count) == 2
try expect(spec.targets) == [target_iOS, target_tvOS]
}

$0.it("parses schemes") {
let schemeDictionary: [String: Any] = [
"build": ["targets": [
Expand Down Expand Up @@ -138,5 +160,6 @@ func specLoadingTests() {
let parsedSpec = try getProjectSpec(["options": ["carthageBuildPath": "../Carthage/Build"]])
try expect(parsedSpec) == expected
}

}
}
24 changes: 24 additions & 0 deletions docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ This will provide default build settings for a certain platform. It can be any o
- macOS
- watchOS

**Multi Platform targets**

You can also specify an array of platforms. This will generate a target for each platform.
If you reference the string `$platform` anywhere within the target spec, that will be replaced with the platform.

The generated targets by default will have a suffix of `_$platform` applied, you can change this by specifying a `platformSuffix` or `platformPrefix`.

If no `PRODUCT_NAME` build setting is specified for a target, this will be set to the target name, so that this target can be imported under a single name.

```
name: MyFramework
sources: MyFramework
platform: [iOS, tvOS]
type: framework
settings:
base:
INFOPLIST_FILE: MyApp/Info.plist
PRODUCT_BUNDLE_IDENTIFIER: com.myapp
MY_SETTING: platform $platform
presets:
- $platform
```
The above will generate 2 targets named `MyFramework_iOS` and `MyFramework_tvOS`, with all the relevant platform build settings. They will both have a `PRODUCT_NAME` of `MyFramework`
### Sources
Specifies the source directories for a target. This can either be a single path or a list of paths. Applicable source files, resources, headers, and lproj files will be parsed appropriately
Expand Down

0 comments on commit 9a93189

Please sign in to comment.