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

Schemes #201

Merged
merged 5 commits into from
Dec 21, 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
66 changes: 63 additions & 3 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ Required properties are marked 🔵 and optional properties with ⚪️.
- [Build Script](#build-script)
- [Dependency](#dependency)
- [Target Scheme](#target-scheme)
- [Legacy Target](#legacy-target)
- [Scheme](#scheme)

## Project

- 🔵 **name**: `String` - Name of the generated project
- ⚪️ **include**: [Include](#include) - One or more paths to other specs
- ⚪️ **options**: [Options](#options) - Various options to override default behaviour
- ⚪️ **attributes**: `map` - The PBXProject attributes. This is for advanced use. Defaults to ``{"LastUpgradeCheck": "0900"}``
- ⚪️ **attributes**: `map` - The PBXProject attributes. This is for advanced use. This defaults to ``{"LastUpgradeCheck": "XcodeVersion"}`` with `xcodeVersion` being set by `options.xcodeVersion`
- ⚪️ **configs**: [Configs](#configs) - Project build configurations. Defaults to `Debug` and `Release` configs
- ⚪️ **configFiles**: [Config Files](#config-files) - `.xcconfig` files per config
- ⚪️ **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 mapped by name
- ⚪️ **fileGroups**: `[String]` - A list of paths to add to the top level groups. These are files that aren't build files but that you'd like in the project hierachy. For example a folder xcconfig files that aren't already added by any target sources.
- ⚪️ **schemes**: [Scheme](#scheme) - A list of schemes by name. This allows more control over what is found in [Target Scheme](#target-scheme)

### Include
One or more specs can be included in the project spec. This can be used to split your project spec into multiple files, for easier structuring or sharing between multiple specs. Included specs can also include other specs and so on.
Expand Down Expand Up @@ -333,11 +336,11 @@ targets:
```

### Target Scheme
This is a convenience used to automatically generate schemes for a target based on different configs or included tests.
This is a convenience used to automatically generate schemes for a target based on different configs or included tests. If you want more control check out the top level [Scheme](#scheme).

- 🔵 **configVariants**: `[String]` - This generates a scheme for each entry, using configs that contain the name with debug and release variants. This is useful for having different environment schemes.
- ⚪️ **testTargets**: `[String]` - a list of test targets that should be included in the scheme. These will be added to the build targets and the test entries
- ⚪️ **gatherCoverageData**: `Bool` - a boolean that indicates if this scheme should gather coverage data
- ⚪️ **gatherCoverageData**: `Bool` - a boolean that indicates if this scheme should gather coverage data. This defaults to false
- ⚪️ **commandLineArguments**: `[String:Bool]` - a dictionary from the argument name (`String`) to if it is enabled (`Bool`). These arguments will be added to the Test, Profile and Run scheme actions

For example, the spec below would create 3 schemes called:
Expand Down Expand Up @@ -381,3 +384,60 @@ By providing a legacy target, you are opting in to the "Legacy Target" mode. Thi
- ⚪️ **arguments**: String - Build arguments used for the build tool in the legacy target
- ⚪️ **passSettings**: Bool - Whether or not to pass build settings down to the build tool in the legacy target.
- ⚪️ **workingDirectory**: String - The working directory under which the build tool will be invoked in the legacy target.

## Scheme

Schemes allows for more control than the convenience [Target Scheme](#target-scheme) on [Target](#target)

- 🔵 **build**: Build options
- ⚪️ **run**: The run action
- ⚪️ **test**: The test action
- ⚪️ **profile**: The profile action
- ⚪️ **analyze**: The analyze action
- ⚪️ **archive**: The archive action

### Build
- 🔵 **targets**: `[Build Target]` - A list of targets to build
- 🔵 **name**: `String` - the name of the target
- ⚪️ **buildTypes**: `[String]` - A list of build types for this target. The default is all types. The build types are:
- `run`
- `test`
- `profile`
- `analyze`
- `archive`

### Common Build Action options
The different actions share some properties:

- ⚪️ **config**: `String` - All build actions can be set to use a certain config. If this is not set the first configuration found of a certain type wil be used, according to the following:
- run, test, analyze: `debug`
- profile, archive: `release`
- ⚪️ **commandLineArguments**: `[String:Bool]` - `run`, `test` and `profile` actions have a map of command line arguments to whether they are enabled

### Test Action
- ⚪️ **gatherCoverageData**: `Bool` - a boolean that indicates if this scheme should gather coverage data. This defaults to false
- ⚪️ **targets**: `[String]` - a list of targets to test

```
scheme:
Production:
build:
targets:
- name: MyTarget1
- name: MyTarget2
buildTypes: [run, archive]
run:
config: prod-debug
commandLineArguments: "--option value"
test:
config: prod-debug
commandLineArguments: "--option testValue"
gatherCoverageData: true
targets: [Tester1, Tester2]
profile:
config: prod-release
analyze:
config: prod-debug
archive:
config: prod-release
```
46 changes: 17 additions & 29 deletions Sources/ProjectSpec/Scheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,6 @@ public struct Scheme: Equatable {
self.archive = archive
}

public init(name: String, targets: [BuildTarget], debugConfig: String, releaseConfig: String, gatherCoverageData: Bool = false, commandLineArguments: [String: Bool] = [:], testTargets: [String] = []) {
self.init(
name: name,
build: .init(targets: targets),
run: .init(config: debugConfig, commandLineArguments: commandLineArguments),
test: .init(config: debugConfig, gatherCoverageData: gatherCoverageData, commandLineArguments: commandLineArguments, targets: testTargets),
profile: .init(config: releaseConfig, commandLineArguments: commandLineArguments),
analyze: .init(config: debugConfig),
archive: .init(config: releaseConfig)
)
}

public struct Build: Equatable {
public var targets: [BuildTarget]
public init(targets: [BuildTarget]) {
Expand Down Expand Up @@ -211,25 +199,25 @@ extension Scheme.BuildTarget: JSONObjectConvertible {
target = try jsonDictionary.json(atKeyPath: "target")
if jsonDictionary["buildTypes"] == nil {
buildTypes = BuildType.all
} else {
if let types: String = jsonDictionary.json(atKeyPath: "buildTypes") {
switch types {
case "all": buildTypes = BuildType.all
case "none": buildTypes = []
case "testing": buildTypes = [.testing, .analyzing]
case "indexing": buildTypes = [.testing, .analyzing, .archiving]
default: buildTypes = BuildType.all
}
} else {
let types: [String: Bool] = try jsonDictionary.json(atKeyPath: "buildTypes")
var buildTypes: [BuildType] = []
for (type, build) in types {
if build, let buildType = BuildType.from(jsonValue: type) {
buildTypes.append(buildType)
}
} else if let types: String = jsonDictionary.json(atKeyPath: "buildTypes") {
switch types {
case "all": buildTypes = BuildType.all
case "none": buildTypes = []
case "testing": buildTypes = [.testing, .analyzing]
case "indexing": buildTypes = [.testing, .analyzing, .archiving]
default: buildTypes = BuildType.all
}
} else if let types: [String: Bool] = jsonDictionary.json(atKeyPath: "buildTypes") {
var buildTypes: [BuildType] = []
for (type, build) in types {
if build, let buildType = BuildType.from(jsonValue: type) {
buildTypes.append(buildType)
}
self.buildTypes = buildTypes
}
self.buildTypes = buildTypes
} else {
let stringBuildTypes: [String] = try jsonDictionary.json(atKeyPath: "buildTypes")
self.buildTypes = stringBuildTypes.flatMap(BuildType.from)
}
}
}
Expand Down
32 changes: 23 additions & 9 deletions Sources/XcodeGenKit/ProjectGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,28 +108,28 @@ public class ProjectGenerator {
}

for target in spec.targets {
if let scheme = target.scheme {
if let targetScheme = target.scheme {

if scheme.configVariants.isEmpty {
if targetScheme.configVariants.isEmpty {
let schemeName = target.name

let debugConfig = spec.configs.first { $0.type == .debug }!
let releaseConfig = spec.configs.first { $0.type == .release }!

let specScheme = Scheme(name: schemeName, targets: [Scheme.BuildTarget(target: target.name)], debugConfig: debugConfig.name, releaseConfig: releaseConfig.name, gatherCoverageData: scheme.gatherCoverageData, commandLineArguments: scheme.commandLineArguments, testTargets: scheme.testTargets)
let scheme = try generateScheme(specScheme, pbxProject: pbxProject)
xcschemes.append(scheme)
let scheme = Scheme(name: schemeName, target: target, targetScheme: targetScheme, debugConfig: debugConfig.name, releaseConfig: releaseConfig.name)
let xcscheme = try generateScheme(scheme, pbxProject: pbxProject)
xcschemes.append(xcscheme)
} else {
for configVariant in scheme.configVariants {
for configVariant in targetScheme.configVariants {

let schemeName = "\(target.name) \(configVariant)"

let debugConfig = spec.configs.first { $0.type == .debug && $0.name.contains(configVariant) }!
let releaseConfig = spec.configs.first { $0.type == .release && $0.name.contains(configVariant) }!

let specScheme = Scheme(name: schemeName, targets: [Scheme.BuildTarget(target: target.name)], debugConfig: debugConfig.name, releaseConfig: releaseConfig.name, gatherCoverageData: scheme.gatherCoverageData, commandLineArguments: scheme.commandLineArguments, testTargets: scheme.testTargets)
let scheme = try generateScheme(specScheme, pbxProject: pbxProject)
xcschemes.append(scheme)
let scheme = Scheme(name: schemeName, target: target, targetScheme: targetScheme, debugConfig: debugConfig.name, releaseConfig: releaseConfig.name)
let xcscheme = try generateScheme(scheme, pbxProject: pbxProject)
xcschemes.append(xcscheme)
}
}
}
Expand All @@ -139,3 +139,17 @@ public class ProjectGenerator {
}
}

extension Scheme {
public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(targets: [Scheme.BuildTarget(target: target.name)]),
run: .init(config: debugConfig, commandLineArguments: targetScheme.commandLineArguments),
test: .init(config: debugConfig, gatherCoverageData: targetScheme.gatherCoverageData, commandLineArguments: targetScheme.commandLineArguments, targets: targetScheme.testTargets),
profile: .init(config: releaseConfig, commandLineArguments: targetScheme.commandLineArguments),
analyze: .init(config: debugConfig),
archive: .init(config: releaseConfig)
)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Scheme LastUpgradeVersion="0920" version="1.3">
<AnalyzeAction buildConfiguration="Production Debug" />
<ArchiveAction buildConfiguration="Production Release" revealArchiveInOrganizer="YES" />
<TestAction selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" codeCoverageEnabled="YES" shouldUseLaunchSchemeArgsEnv="YES" buildConfiguration="Production Debug">
<TestAction selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" codeCoverageEnabled="YES" shouldUseLaunchSchemeArgsEnv="NO" buildConfiguration="Production Debug">
<Testables>
<TestableReference skipped="NO">
<BuildableReference BlueprintIdentifier="T_7831228999101" ReferencedContainer="container:Project.xcodeproj" BuildableName="App_iOS_Tests.xctest" BlueprintName="App_iOS" BuildableIdentifier="primary" />
Expand All @@ -16,7 +16,7 @@
<CommandLineArgument argument="MyDisabledArgument" isEnabled="NO" />
</CommandLineArguments>
</TestAction>
<ProfileAction savedToolIdentifier="" useCustomWorkingDirectory="NO" shouldUseLaunchSchemeArgsEnv="YES" buildConfiguration="Production Release" debugDocumentVersioning="YES">
<ProfileAction savedToolIdentifier="" useCustomWorkingDirectory="NO" shouldUseLaunchSchemeArgsEnv="NO" buildConfiguration="Production Release" debugDocumentVersioning="YES">
<BuildableProductRunnable runnableDebuggingMode="0">
<BuildableReference BlueprintIdentifier="T_8252321105004" ReferencedContainer="container:Project.xcodeproj" BuildableName="App_iOS.app" BlueprintName="App_iOS" BuildableIdentifier="primary" />
</BuildableProductRunnable>
Expand All @@ -30,9 +30,6 @@
<BuildActionEntry buildForArchiving="YES" buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForAnalyzing="YES">
<BuildableReference BlueprintIdentifier="T_8252321105004" ReferencedContainer="container:Project.xcodeproj" BuildableName="App_iOS.app" BlueprintName="App_iOS" BuildableIdentifier="primary" />
</BuildActionEntry>
<BuildActionEntry buildForArchiving="NO" buildForTesting="YES" buildForRunning="NO" buildForProfiling="NO" buildForAnalyzing="YES">
<BuildableReference BlueprintIdentifier="T_7831228999101" ReferencedContainer="container:Project.xcodeproj" BuildableName="App_iOS_Tests.xctest" BlueprintName="App_iOS" BuildableIdentifier="primary" />
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<LaunchAction selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle="0" buildConfiguration="Production Debug" debugServiceExtension="internal" ignoresPersistentStateOnLaunch="NO" useCustomWorkingDirectory="NO" allowLocationSimulation="YES" debugDocumentVersioning="YES">
Expand Down
7 changes: 4 additions & 3 deletions Tests/XcodeGenKitTests/ProjectSpecTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ func projectSpecTests() {
$0.it("fails with invalid scheme") {
var spec = baseSpec
spec.schemes = [Scheme(name: "scheme1",
targets: [Scheme.BuildTarget(target: "invalidTarget")],
debugConfig: "debugInvalid",
releaseConfig: "releaseInvalid")]
build: .init(targets: [.init(target: "invalidTarget")]),
run: .init(config: "debugInvalid"),
archive: .init(config: "releaseInvalid")
)]

try expectValidationError(spec, .invalidSchemeTarget(scheme: "scheme1", target: "invalidTarget"))
try expectValidationError(spec, .invalidSchemeConfig(scheme: "scheme1", config: "debugInvalid"))
Expand Down
2 changes: 2 additions & 0 deletions Tests/XcodeGenKitTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ func specLoadingTests() {
["target": "Target", "buildTypes": "all"],
["target": "Target", "buildTypes": ["testing": true]],
["target": "Target", "buildTypes": ["testing": false]],
["target": "Target", "buildTypes": ["test", "analyze"]],
]],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
Expand All @@ -173,6 +174,7 @@ func specLoadingTests() {
Scheme.BuildTarget(target: "Target", buildTypes: BuildType.all),
Scheme.BuildTarget(target: "Target", buildTypes: [.testing]),
Scheme.BuildTarget(target: "Target", buildTypes: []),
Scheme.BuildTarget(target: "Target", buildTypes: [.testing, .analyzing]),
]
try expect(scheme.name) == "Scheme"
try expect(scheme.build.targets) == expectedTargets
Expand Down