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

Generate multi platform targets #35

Merged
merged 1 commit into from
Aug 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
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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great @yonaskolb. Nice job 😛

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