From 38bc3be64b94a15d9bb2ea6faec7b13c6966f0d1 Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Thu, 14 Sep 2017 20:53:34 +0200 Subject: [PATCH 1/3] change yaml targets from list to map --- .../project.pbxproj | 8 ++-- Fixtures/TestProject/spec.yml | 6 +-- Fixtures/include_test.yml | 2 +- Fixtures/included.yml | 2 +- Fixtures/settings_test.yml | 2 +- README.md | 4 +- Sources/ProjectSpec/ProjectSpec.swift | 7 ++- Sources/ProjectSpec/Target.swift | 39 ++++++++-------- Tests/XcodeGenKitTests/SpecLoadingTests.swift | 11 ++--- docs/ProjectSpec.md | 46 ++++++++++--------- 10 files changed, 65 insertions(+), 62 deletions(-) diff --git a/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj b/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj index 882a9a85..3476b48f 100644 --- a/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj +++ b/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj @@ -83,7 +83,7 @@ isa = PBXGroup; children = ( FR7078510801 /* FrameworkFile.swift */, - FR1345298502 /* Info.plist */, + FR1345298501 /* Info.plist */, FR7740960501 /* MyFramework.h */, ); name = MyFramework; @@ -95,7 +95,7 @@ children = ( FR1332263601 /* AppDelegate.swift */, FR5980633301 /* Assets.xcassets */, - FR1345298501 /* Info.plist */, + FR1345298502 /* Info.plist */, FR6218091901 /* ViewController.swift */, VG6405436301 /* LaunchScreen.storyboard */, VG1473702401 /* Main.storyboard */, @@ -107,8 +107,8 @@ G84487712001 = { isa = PBXGroup; children = ( - G65232631501 /* TestProject */, G29934978701 /* MyFramework */, + G65232631501 /* TestProject */, G26536595301 /* TestProjectTests */, G86202385201 /* Products */, ); @@ -223,8 +223,8 @@ ); mainGroup = G84487712001; targets = ( - NT6523263101 /* TestProject */, NT2993497801 /* MyFramework */, + NT6523263101 /* TestProject */, NT2653659501 /* TestProjectTests */, ); }; diff --git a/Fixtures/TestProject/spec.yml b/Fixtures/TestProject/spec.yml index 4f07755a..faf55eec 100644 --- a/Fixtures/TestProject/spec.yml +++ b/Fixtures/TestProject/spec.yml @@ -1,6 +1,6 @@ name: GeneratedProject targets: - - name: TestProject + TestProject: type: application platform: iOS sources: TestProject @@ -23,7 +23,7 @@ targets: else echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" fi - - name: MyFramework + MyFramework: type: framework platform: iOS sources: MyFramework @@ -32,7 +32,7 @@ targets: postbuildScripts: - name: Swiftlint path: scripts/swiftlint.sh - - name: TestProjectTests + TestProjectTests: type: bundle.unit-test platform: iOS sources: TestProjectTests diff --git a/Fixtures/include_test.yml b/Fixtures/include_test.yml index 1db83756..a889d980 100644 --- a/Fixtures/include_test.yml +++ b/Fixtures/include_test.yml @@ -7,6 +7,6 @@ settingGroups: new: MY_SETTING: VALUE targets: - - name: NewTarget + NewTarget: type: application platform: iOS diff --git a/Fixtures/included.yml b/Fixtures/included.yml index 6f5cd6db..1619d813 100644 --- a/Fixtures/included.yml +++ b/Fixtures/included.yml @@ -4,6 +4,6 @@ settingGroups: MY_SETTING1: VALUE1 MY_SETTING2: VALUE2 targets: - - name: IncludedTarget + IncludedTarget: type: application platform: iOS diff --git a/Fixtures/settings_test.yml b/Fixtures/settings_test.yml index 5b07f38f..ae7cf20d 100644 --- a/Fixtures/settings_test.yml +++ b/Fixtures/settings_test.yml @@ -68,7 +68,7 @@ configs: config1: debug config2: release targets: - - name: Target + Target: type: application platform: iOS settings: diff --git a/README.md b/README.md index b7ea0c55..1d531401 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Given a very simple project spec file like this: ```yaml name: My Project targets: - - name: MyApp + MyApp: type: application platform: iOS sources: MyApp @@ -46,7 +46,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.myapp dependencies: - target: MyFramework - - name: MyFramework + MyFramework: type: framework platform: iOS sources: MyFramework diff --git a/Sources/ProjectSpec/ProjectSpec.swift b/Sources/ProjectSpec/ProjectSpec.swift index 03f5518f..5f25bcf2 100644 --- a/Sources/ProjectSpec/ProjectSpec.swift +++ b/Sources/ProjectSpec/ProjectSpec.swift @@ -74,12 +74,13 @@ extension ProjectSpec.Options: Equatable { extension ProjectSpec: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { + let jsonDictionary = try ProjectSpec.filterJSON(jsonDictionary: jsonDictionary) name = try jsonDictionary.json(atKeyPath: "name") settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty settingGroups = jsonDictionary.json(atKeyPath: "settingGroups") ?? jsonDictionary.json(atKeyPath: "settingPresets") ?? [:] let configs: [String: String] = jsonDictionary.json(atKeyPath: "configs") ?? [:] self.configs = configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) } - targets = try Target.decodeTargets(jsonDictionary: jsonDictionary) + targets = try jsonDictionary.json(atKeyPath: "targets").sorted { $0.name < $1.name } schemes = try jsonDictionary.json(atKeyPath: "schemes") attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:] if jsonDictionary["options"] != nil { @@ -88,6 +89,10 @@ extension ProjectSpec: JSONObjectConvertible { options = Options() } } + + static func filterJSON(jsonDictionary: JSONDictionary) throws -> JSONDictionary { + return try Target.generateCrossPlaformTargets(jsonDictionary: jsonDictionary) + } } extension ProjectSpec.Options: JSONObjectConvertible { diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index 01de3d21..b9049242 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -48,22 +48,20 @@ public struct Target { extension Target { - static func decodeTargets(jsonDictionary: JSONDictionary) throws -> [Target] { - guard jsonDictionary["targets"] != nil else { - return [] + static func generateCrossPlaformTargets(jsonDictionary: JSONDictionary) throws -> JSONDictionary { + guard let targetsDictionary: [String: JSONDictionary] = jsonDictionary["targets"] as? [String: JSONDictionary] else { + return jsonDictionary } - let array: [JSONDictionary] = try jsonDictionary.json(atKeyPath: "targets", invalidItemBehaviour: .fail) - - var targets: [JSONDictionary] = [] let platformReplacement = "$platform" + var crossPlatformTargets: [String: JSONDictionary] = [:] - for json in array { + for (targetName, target) in targetsDictionary { - if let platforms = json["platform"] as? [String] { + if let platforms = target["platform"] as? [String] { for platform in platforms { - var platformTarget = json + var platformTarget = target func replacePlatform(_ dictionary: JSONDictionary) -> JSONDictionary { var replaced = dictionary @@ -88,31 +86,30 @@ extension Target { 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 + let newTargetName = platformPrefix + targetName + platformSuffix var settings = platformTarget["settings"] as? JSONDictionary ?? [:] if settings["configs"] != nil || settings["groups"] != nil || settings["base"] != nil { var base = settings["base"] as? JSONDictionary ?? [:] if base["PRODUCT_NAME"] == nil { - base["PRODUCT_NAME"] = name + base["PRODUCT_NAME"] = targetName } settings["base"] = base } else { if settings["PRODUCT_NAME"] == nil { - settings["PRODUCT_NAME"] = name + settings["PRODUCT_NAME"] = targetName } } platformTarget["settings"] = settings - - targets.append(platformTarget) + crossPlatformTargets[newTargetName] = platformTarget } } else { - targets.append(json) + crossPlatformTargets[targetName] = target } } - - return try targets.map { try Target(jsonDictionary: $0) } + var merged = jsonDictionary + merged["targets"] = crossPlatformTargets + return merged } } @@ -159,10 +156,10 @@ extension TargetScheme: JSONObjectConvertible { } } -extension Target: JSONObjectConvertible { +extension Target: NamedJSONDictionaryConvertible { - public init(jsonDictionary: JSONDictionary) throws { - name = try jsonDictionary.json(atKeyPath: "name") + public init(name: String, jsonDictionary: JSONDictionary) throws { + self.name = name let typeString: String = try jsonDictionary.json(atKeyPath: "type") if let type = PBXProductType(string: typeString) { self.type = type diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 27bebc00..ae7e0c0c 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -23,11 +23,11 @@ func specLoadingTests() { func expectTargetError(_ target: [String: Any], _ expectedError: ProjectSpecError) throws { try expectError(expectedError) { - _ = try Target(jsonDictionary: target) + _ = try Target(name: "test", jsonDictionary: target) } } - let validTarget: [String: Any] = ["name": "test", "type": "application", "platform": "iOS"] + let validTarget: [String: Any] = ["type": "application", "platform": "iOS"] let invalid = "invalid" describe("Spec Loader") { @@ -74,7 +74,7 @@ func specLoadingTests() { ["carthage": "name"], ["framework": "path"], ] - let target = try Target(jsonDictionary: targetDictionary) + let target = try Target(name: "test", jsonDictionary: targetDictionary) try expect(target.dependencies.count) == 3 try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false) try expect(target.dependencies[1]) == Dependency(type: .carthage, reference: "name") @@ -83,14 +83,13 @@ func specLoadingTests() { $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]]) + let spec = try getProjectSpec(["targets": ["Framework": targetDictionary]]) var target_iOS = Target(name: "Framework_iOS", type: .framework, platform: .iOS) var target_tvOS = Target(name: "Framework_tvOS", type: .framework, platform: .tvOS) @@ -167,7 +166,7 @@ func specLoadingTests() { BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: true), ] - let parsedTarget = try Target(jsonDictionary: target) + let parsedTarget = try Target(name: "test", jsonDictionary: target) try expect(parsedTarget.prebuildScripts) == expectedScripts try expect(parsedTarget.postbuildScripts) == expectedScripts } diff --git a/docs/ProjectSpec.md b/docs/ProjectSpec.md index 50c37da4..e815f2bc 100644 --- a/docs/ProjectSpec.md +++ b/docs/ProjectSpec.md @@ -31,7 +31,7 @@ Required properties are marked 🔵 and optional properties with ⚪️. - ⚪️ **configs**: [Configs](#configs) - Project build configurations. Defaults to `Debug` and `Release` configs - ⚪️ **settings**: [Settings](#settings) - Project specific settings. Default base and config type settings will be applied first before any settings defined here - ⚪️ **settingGroups**: [Setting Groups](#setting-groups) - Setting groups mapped by name -- ⚪️ **targets**: [[Target](#target)] - The list of targets in the project +- ⚪️ **targets**: [String: [Target](#target)] - The list of targets in the project mapped by name ### Options - ⚪️ **carthageBuildPath**: `String` - The path to the carthage build directory. Defaults to `Carthage/Build`. This is used when specifying target carthage dependencies @@ -94,7 +94,6 @@ Settings are merged in the following order: groups, base, configs. ## Target -- 🔵 **name**: `String` - Name of the target - 🔵 **type**: [Product Type](#product-type) - Product type of the target - 🔵 **platform**: [Platform](#platform) - Platform of the target - ⚪️ **sources**: [Sources](#sources) - Source directories of the target @@ -146,18 +145,19 @@ The generated targets by default will have a suffix of `_$platform` applied, you 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 - groups: - - $platform +```yaml +targets: + MyFramework: + sources: MyFramework + platform: [iOS, tvOS] + type: framework + settings: + base: + INFOPLIST_FILE: MyApp/Info.plist + PRODUCT_BUNDLE_IDENTIFIER: com.myapp + MY_SETTING: platform $platform + groups: + - $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` @@ -166,9 +166,9 @@ Specifies the source directories for a target. This can either be a single path ```yaml targets: - - name: MyTarget + MyTarget sources: MyTargetSource - - name: MyOtherTarget + MyOtherTarget sources: - MyOtherTargetSource1 - MyOtherTargetSource2 @@ -201,12 +201,13 @@ If any applications contain carthage dependencies within itself or any dependent ```yaml targets: - - name: MyTarget + MyTarget: dependencies: - target: MyFramework - framework: path/to/framework.framework - carthage: Result - - name: MyFramework + MyFramework: + type: framework ``` ### Config Files @@ -214,7 +215,7 @@ Specifies `.xcconfig` files for each configuration. ```yaml targets: - - name: MyTarget + MyTarget: configFiles: Debug: config_files/debug.xcconfig Release: config_files/release.xcconfig @@ -237,7 +238,7 @@ A multiline script can be written using the various YAML multiline methods, for ```yaml targets: - - name: MyTarget + MyTarget: prebuildScripts: - path: myscripts/my_script.sh name: My Script @@ -279,7 +280,7 @@ configs: Staging Release: release Production Release: release targets - - name: MyApp + MyApp: scheme: testTargets: - MyUnitTests @@ -287,5 +288,6 @@ targets - Test - Staging - Production - - name: MyUnitTests + MyUnitTests: + sources: Tests ``` From d5204199f36b325485cd6e8ce14680a96b9c059a Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Thu, 14 Sep 2017 21:00:52 +0200 Subject: [PATCH 2/3] add target modification in include test --- Fixtures/include_test.yml | 2 ++ Tests/XcodeGenKitTests/SpecLoadingTests.swift | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Fixtures/include_test.yml b/Fixtures/include_test.yml index a889d980..3baac572 100644 --- a/Fixtures/include_test.yml +++ b/Fixtures/include_test.yml @@ -10,3 +10,5 @@ targets: NewTarget: type: application platform: iOS + IncludedTarget: + platform: tvOS diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index ae7e0c0c..49d81eb2 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -41,7 +41,7 @@ func specLoadingTests() { "new": Settings(dictionary: ["MY_SETTING": "VALUE"]), ] try expect(spec.targets) == [ - Target(name: "IncludedTarget", type: .application, platform: .iOS), + Target(name: "IncludedTarget", type: .application, platform: .tvOS), Target(name: "NewTarget", type: .application, platform: .iOS), ] } From 061bddd18b387ec3c4d64b6a87710d5532918439 Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Thu, 14 Sep 2017 21:04:33 +0200 Subject: [PATCH 3/3] ability to override target name --- Fixtures/include_test.yml | 2 ++ Sources/ProjectSpec/Target.swift | 2 +- Tests/XcodeGenKitTests/SpecLoadingTests.swift | 2 +- docs/ProjectSpec.md | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Fixtures/include_test.yml b/Fixtures/include_test.yml index 3baac572..02fa473e 100644 --- a/Fixtures/include_test.yml +++ b/Fixtures/include_test.yml @@ -11,4 +11,6 @@ targets: type: application platform: iOS IncludedTarget: + name: IncludedTargetNew platform: tvOS + sources: Target diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index b9049242..438a0797 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -159,7 +159,7 @@ extension TargetScheme: JSONObjectConvertible { extension Target: NamedJSONDictionaryConvertible { public init(name: String, jsonDictionary: JSONDictionary) throws { - self.name = name + self.name = jsonDictionary.json(atKeyPath: "name") ?? name let typeString: String = try jsonDictionary.json(atKeyPath: "type") if let type = PBXProductType(string: typeString) { self.type = type diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 49d81eb2..8a10deaf 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -41,7 +41,7 @@ func specLoadingTests() { "new": Settings(dictionary: ["MY_SETTING": "VALUE"]), ] try expect(spec.targets) == [ - Target(name: "IncludedTarget", type: .application, platform: .tvOS), + Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["Target"]), Target(name: "NewTarget", type: .application, platform: .iOS), ] } diff --git a/docs/ProjectSpec.md b/docs/ProjectSpec.md index e815f2bc..898b8ff0 100644 --- a/docs/ProjectSpec.md +++ b/docs/ProjectSpec.md @@ -25,7 +25,7 @@ Required properties are marked 🔵 and optional properties with ⚪️. ## Project - 🔵 **name**: `String` - Name of the generated project -- ⚪️ **include**: `[String]` - The paths to other specs. They will be merged in order and then the current spec will be merged on top +- ⚪️ **include**: `[String]` - The paths to other specs. They will be merged in order and then the current spec will be merged on top. Target names can be changed by adding a `name` property - ⚪️ **options**: [Options](#options) - Various options to override default behaviour - ⚪️ **attributes**: `map` - The PBXProject attributes. This is for advanced use. Defaults to ``{"LastUpgradeCheck": "0830"}`` - ⚪️ **configs**: [Configs](#configs) - Project build configurations. Defaults to `Debug` and `Release` configs