mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2024-09-11 12:56:03 +03:00
* Resolves #173 - Shared breakpoints support * Added breakpoints full documentation * Invalid breakpoint just throw JSONUtilities decoding error. * Use enumeration types instead of String for extensionIDs * Remove a necessary line * Remove unnecessary custom Equatable implementation * Update CHANGELOG.md * Ignore empty breakpoints * Update Docs/ProjectSpec.md Fix a typo Co-Authored-By: Yonas Kolb <yonaskolb@users.noreply.github.com> * Change some properties that should be Int to Int * Create 2 typealiases * Use BreakpointType where it is missing * Remove unused Location * Change some names * Add Breakpoint.Scope * Add Breakpoint.StopOnStyle * Change the type of the raw value to String * Remove some properties that may cause confusing * Require filePah and line when the type is .file * Add tests about decoding breakpoints * Add Breakpoint.Action.ConveyanceType * Add default value for waitUntilDone * Add Breakpoint.Action.SoundName * Add tests about decoding breakpoint actions * Fix some issues in ProjectSpec.md * Improve ProjectSpec.md * Add missing condition * Add breakpoints to project.yml * Use unwarp * Remove the Breakpoint suffix * Refactor BreakpointType * Refactor Breakpoint.Action * Remove unnecessary properties * Adjust the line wrapping style for BreakpointGenerator * Support column breakpoints --------- Co-authored-by: Alex Rupérez <alejandro.ruperez@intelygenz.com> Co-authored-by: Yonas Kolb <yonaskolb@users.noreply.github.com>
This commit is contained in:
parent
aa7fed0f5b
commit
d1dd93aac4
@ -2,6 +2,10 @@
|
||||
|
||||
## Next Version
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for shared breakpoints #177 @alexruperez @myihsan
|
||||
|
||||
## 2.34.0
|
||||
|
||||
### Changed
|
||||
|
@ -55,6 +55,7 @@ You can also use environment variables in your configuration file, by using `${S
|
||||
- [ ] **include**: **[Include](#include)** - One or more paths to other specs
|
||||
- [ ] **options**: **[Options](#options)** - Various options to override default behaviour
|
||||
- [ ] **attributes**: **[String: Any]** - The PBXProject attributes. This is for advanced use. If no value is set for `LastUpgradeCheck`, it will be defaulted to ``{"LastUpgradeCheck": "XcodeVersion"}`` with `xcodeVersion` being set by [Options](#options)`.xcodeVersion`
|
||||
- [ ] **breakpoints**: [Breakpoints](#breakpoints) - Add shared breakpoints to the generated project
|
||||
- [ ] **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
|
||||
@ -181,6 +182,81 @@ Default settings for file extensions. See [Sources](#sources) for more documenta
|
||||
- [ ] **resourceTags**: **[String]** - On Demand Resource Tags that will be applied to any resources. This also adds to the project attribute's knownAssetTags.
|
||||
- [ ] **compilerFlags**: **[String]** - A list of compiler flags to add.
|
||||
|
||||
### Breakpoints
|
||||
|
||||
- [x] **type**: **String** - Breakpoint type
|
||||
- `File`: file breakpoint
|
||||
- `Exception`: exception breakpoint
|
||||
- `SwiftError`: swift error breakpoint
|
||||
- `OpenGLError`: OpenGL breakpoint
|
||||
- `Symbolic`: symbolic breakpoint
|
||||
- `IDEConstraintError`: IDE constraint breakpoint
|
||||
- `IDETestFailure`: IDE test failure breakpoint
|
||||
- [ ] **enabled**: **Bool** - Indicates whether it should be active. Default to `true`
|
||||
- [ ] **ignoreCount**: **Int** - Indicates how many times it should be ignored before stopping, Default to `0`
|
||||
- [ ] **continueAfterRunningActions**: **Bool** - Indicates if should automatically continue after evaluating actions, Default to `false`
|
||||
- [ ] **path**: **String** - Breakpoint file path (only required by file breakpoints)
|
||||
- [ ] **line**: **Int** - Breakpoint line (only required by file breakpoints)
|
||||
- [ ] **symbol**: **String** - Breakpoint symbol (only used by symbolic breakpoints)
|
||||
- [ ] **module**: **String** - Breakpoint module (only used by symbolic breakpoints)
|
||||
- [ ] **scope**: **String** - Breakpoint scope (only used by exception breakpoints)
|
||||
- `All`
|
||||
- `Objective-C` (default)
|
||||
- `C++`
|
||||
- [ ] **stopOnStyle**: **String** - Indicates if should stop on style (only used by exception breakpoints)
|
||||
-`throw` (default)
|
||||
-`catch`
|
||||
- [ ] **condition**: **String** - Breakpoint condition
|
||||
- [ ] **actions**: **[[Breakpoint Action](#breakpoint-action)]** - breakpoint actions
|
||||
|
||||
```yaml
|
||||
breakpoints:
|
||||
- type: ExceptionBreakpoint
|
||||
enabled: true
|
||||
ignoreCount: 0
|
||||
continueAfterRunningActions: false
|
||||
```
|
||||
|
||||
#### Breakpoint Action
|
||||
|
||||
- [x] **type**: **String** - Breakpoint action type
|
||||
- `DebuggerCommand`: execute debugger command
|
||||
- `Log`: log message
|
||||
- `ShellCommand`: execute shell command
|
||||
- `GraphicsTrace`: capture GPU frame
|
||||
- `AppleScript`: execute AppleScript
|
||||
- `Sound`: play sound
|
||||
- [ ] **command**: **String** - Debugger command (only used by debugger command breakpoint action)
|
||||
- [ ] **message**: **String** - Log message (only used log message breakpoint action)
|
||||
- [ ] **conveyanceType**: **String** - Conveyance type (only used by log message breakpoint action)
|
||||
- `console`: log message to console (default)
|
||||
- `speak`: speak message
|
||||
- [ ] **path**: **String** - Shell command file path (only used by shell command breakpoint action)
|
||||
- [ ] **arguments**: **String** - Shell command arguments (only used by shell command breakpoint action)
|
||||
- [ ] **waitUntilDone**: **Bool** - Indicates whether it should wait until done (only used by shell command breakpoint action). Default to `false`
|
||||
- [ ] **script**: **String** - AppleScript (only used by AppleScript breakpoint action)
|
||||
- [ ] **sound**: **String** - Sound name (only used by sound breakpoint action)
|
||||
- `Basso` (default)
|
||||
- `Blow`
|
||||
- `Bottle`
|
||||
- `Frog`
|
||||
- `Funk`
|
||||
- `Glass`
|
||||
- `Hero`
|
||||
- `Morse`
|
||||
- `Ping`
|
||||
- `Pop`
|
||||
- `Purr`
|
||||
- `Sosumi`
|
||||
- `Submarine`
|
||||
- `Tink`
|
||||
|
||||
```yaml
|
||||
actions:
|
||||
- type: Sound
|
||||
sound: Blow
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
261
Sources/ProjectSpec/Breakpoint.swift
Normal file
261
Sources/ProjectSpec/Breakpoint.swift
Normal file
@ -0,0 +1,261 @@
|
||||
import Foundation
|
||||
import XcodeProj
|
||||
import JSONUtilities
|
||||
|
||||
public typealias BreakpointActionExtensionID = XCBreakpointList.BreakpointProxy.BreakpointContent.BreakpointActionProxy.ActionExtensionID
|
||||
public typealias BreakpointExtensionID = XCBreakpointList.BreakpointProxy.BreakpointExtensionID
|
||||
|
||||
public struct Breakpoint: Equatable {
|
||||
|
||||
public enum BreakpointType: Equatable {
|
||||
|
||||
public struct Exception: Equatable {
|
||||
|
||||
public enum Scope: String, Equatable {
|
||||
case all = "0"
|
||||
case objectiveC = "1"
|
||||
case cpp = "2"
|
||||
}
|
||||
|
||||
public enum StopOnStyle: String, Equatable {
|
||||
case `throw` = "0"
|
||||
case `catch` = "1"
|
||||
}
|
||||
|
||||
public var scope: Scope
|
||||
public var stopOnStyle: StopOnStyle
|
||||
|
||||
public init(scope: Breakpoint.BreakpointType.Exception.Scope = .objectiveC,
|
||||
stopOnStyle: Breakpoint.BreakpointType.Exception.StopOnStyle = .throw) {
|
||||
self.scope = scope
|
||||
self.stopOnStyle = stopOnStyle
|
||||
}
|
||||
}
|
||||
case file(path: String, line: Int, column: Int?)
|
||||
case exception(Exception)
|
||||
case swiftError
|
||||
case openGLError
|
||||
case symbolic(symbol: String?, module: String?)
|
||||
case ideConstraintError
|
||||
case ideTestFailure
|
||||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
|
||||
public struct Log: Equatable {
|
||||
|
||||
public enum ConveyanceType: String, Equatable {
|
||||
case console = "0"
|
||||
case speak = "1"
|
||||
}
|
||||
|
||||
public var message: String?
|
||||
public var conveyanceType: ConveyanceType
|
||||
|
||||
public init(message: String? = nil, conveyanceType: Breakpoint.Action.Log.ConveyanceType = .console) {
|
||||
self.message = message
|
||||
self.conveyanceType = conveyanceType
|
||||
}
|
||||
}
|
||||
|
||||
public enum Sound: String, Equatable {
|
||||
case basso = "Basso"
|
||||
case blow = "Blow"
|
||||
case bottle = "Bottle"
|
||||
case frog = "Frog"
|
||||
case funk = "Funk"
|
||||
case glass = "Glass"
|
||||
case hero = "Hero"
|
||||
case morse = "Morse"
|
||||
case ping = "Ping"
|
||||
case pop = "Pop"
|
||||
case purr = "Purr"
|
||||
case sosumi = "Sosumi"
|
||||
case submarine = "Submarine"
|
||||
case tink = "Tink"
|
||||
}
|
||||
|
||||
case debuggerCommand(String?)
|
||||
case log(Log)
|
||||
case shellCommand(path: String?, arguments: String?, waitUntilDone: Bool = false)
|
||||
case graphicsTrace
|
||||
case appleScript(String?)
|
||||
case sound(Sound)
|
||||
}
|
||||
|
||||
public var type: BreakpointType
|
||||
public var enabled: Bool
|
||||
public var ignoreCount: Int
|
||||
public var continueAfterRunningActions: Bool
|
||||
public var condition: String?
|
||||
public var actions: [Breakpoint.Action]
|
||||
|
||||
public init(type: BreakpointType,
|
||||
enabled: Bool = true,
|
||||
ignoreCount: Int = 0,
|
||||
continueAfterRunningActions: Bool = false,
|
||||
filePath: String? = nil,
|
||||
line: Int? = nil,
|
||||
condition: String? = nil,
|
||||
actions: [Breakpoint.Action] = []) {
|
||||
self.type = type
|
||||
self.enabled = enabled
|
||||
self.ignoreCount = ignoreCount
|
||||
self.continueAfterRunningActions = continueAfterRunningActions
|
||||
self.condition = condition
|
||||
self.actions = actions
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint.BreakpointType.Exception.Scope {
|
||||
|
||||
public init(string: String) throws {
|
||||
let string = string.lowercased()
|
||||
switch string {
|
||||
case "all":
|
||||
self = .all
|
||||
case "objective-c":
|
||||
self = .objectiveC
|
||||
case "c++":
|
||||
self = .cpp
|
||||
default:
|
||||
throw SpecParsingError.unknownBreakpointScope(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint.BreakpointType.Exception.StopOnStyle {
|
||||
|
||||
public init(string: String) throws {
|
||||
let string = string.lowercased()
|
||||
switch string {
|
||||
case "throw":
|
||||
self = .throw
|
||||
case "catch":
|
||||
self = .catch
|
||||
default:
|
||||
throw SpecParsingError.unknownBreakpointStopOnStyle(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint.Action.Log.ConveyanceType {
|
||||
|
||||
init(string: String) throws {
|
||||
let string = string.lowercased()
|
||||
switch string {
|
||||
case "console":
|
||||
self = .console
|
||||
case "speak":
|
||||
self = .speak
|
||||
default:
|
||||
throw SpecParsingError.unknownBreakpointActionConveyanceType(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint.Action.Sound {
|
||||
|
||||
init(name: String) throws {
|
||||
guard let sound = Self.init(rawValue: name) else {
|
||||
throw SpecParsingError.unknownBreakpointActionSoundName(name)
|
||||
}
|
||||
self = sound
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint.Action: JSONObjectConvertible {
|
||||
|
||||
public init(jsonDictionary: JSONDictionary) throws {
|
||||
let idString: String = try jsonDictionary.json(atKeyPath: "type")
|
||||
let id = try BreakpointActionExtensionID(string: idString)
|
||||
switch id {
|
||||
case .debuggerCommand:
|
||||
let command: String? = jsonDictionary.json(atKeyPath: "command")
|
||||
self = .debuggerCommand(command)
|
||||
case .log:
|
||||
let message: String? = jsonDictionary.json(atKeyPath: "message")
|
||||
let conveyanceType: Log.ConveyanceType
|
||||
if jsonDictionary["conveyanceType"] != nil {
|
||||
let conveyanceTypeString: String = try jsonDictionary.json(atKeyPath: "conveyanceType")
|
||||
conveyanceType = try .init(string: conveyanceTypeString)
|
||||
} else {
|
||||
conveyanceType = .console
|
||||
}
|
||||
self = .log(.init(message: message, conveyanceType: conveyanceType))
|
||||
case .shellCommand:
|
||||
let path: String? = jsonDictionary.json(atKeyPath: "path")
|
||||
let arguments: String? = jsonDictionary.json(atKeyPath: "arguments")
|
||||
let waitUntilDone = jsonDictionary.json(atKeyPath: "waitUntilDone") ?? false
|
||||
self = .shellCommand(path: path, arguments: arguments, waitUntilDone: waitUntilDone)
|
||||
case .graphicsTrace:
|
||||
self = .graphicsTrace
|
||||
case .appleScript:
|
||||
let script: String? = jsonDictionary.json(atKeyPath: "script")
|
||||
self = .appleScript(script)
|
||||
case .sound:
|
||||
let sound: Sound
|
||||
if jsonDictionary["sound"] != nil {
|
||||
let name: String = try jsonDictionary.json(atKeyPath: "sound")
|
||||
sound = try .init(name: name)
|
||||
} else {
|
||||
sound = .basso
|
||||
}
|
||||
self = .sound(sound)
|
||||
case .openGLError:
|
||||
throw SpecParsingError.unknownBreakpointActionType(idString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Breakpoint: JSONObjectConvertible {
|
||||
|
||||
public init(jsonDictionary: JSONDictionary) throws {
|
||||
let idString: String = try jsonDictionary.json(atKeyPath: "type")
|
||||
let id = try BreakpointExtensionID(string: idString)
|
||||
switch id {
|
||||
case .file:
|
||||
let path: String = try jsonDictionary.json(atKeyPath: "path")
|
||||
let line: Int = try jsonDictionary.json(atKeyPath: "line")
|
||||
let column: Int? = jsonDictionary.json(atKeyPath: "column")
|
||||
type = .file(path: path, line: line, column: column)
|
||||
case .exception:
|
||||
let scope: BreakpointType.Exception.Scope
|
||||
if jsonDictionary["scope"] != nil {
|
||||
let scopeString: String = try jsonDictionary.json(atKeyPath: "scope")
|
||||
scope = try .init(string: scopeString)
|
||||
} else {
|
||||
scope = .objectiveC
|
||||
}
|
||||
let stopOnStyle: BreakpointType.Exception.StopOnStyle
|
||||
if jsonDictionary["stopOnStyle"] != nil {
|
||||
let stopOnStyleString: String = try jsonDictionary.json(atKeyPath: "stopOnStyle")
|
||||
stopOnStyle = try .init(string: stopOnStyleString)
|
||||
} else {
|
||||
stopOnStyle = .throw
|
||||
}
|
||||
type = .exception(.init(scope: scope, stopOnStyle: stopOnStyle))
|
||||
case .swiftError:
|
||||
type = .swiftError
|
||||
case .openGLError:
|
||||
type = .openGLError
|
||||
case .symbolic:
|
||||
let symbol: String? = jsonDictionary.json(atKeyPath: "symbol")
|
||||
let module: String? = jsonDictionary.json(atKeyPath: "module")
|
||||
type = .symbolic(symbol: symbol, module: module)
|
||||
case .ideConstraintError:
|
||||
type = .ideConstraintError
|
||||
case .ideTestFailure:
|
||||
type = .ideTestFailure
|
||||
}
|
||||
enabled = jsonDictionary.json(atKeyPath: "enabled") ?? true
|
||||
ignoreCount = jsonDictionary.json(atKeyPath: "ignoreCount") ?? 0
|
||||
continueAfterRunningActions = jsonDictionary.json(atKeyPath: "continueAfterRunningActions") ?? false
|
||||
condition = jsonDictionary.json(atKeyPath: "condition")
|
||||
if jsonDictionary["actions"] != nil {
|
||||
actions = try jsonDictionary.json(atKeyPath: "actions", invalidItemBehaviour: .fail)
|
||||
} else {
|
||||
actions = []
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ public struct Project: BuildSettingsContainer {
|
||||
public var settingGroups: [String: Settings]
|
||||
public var configs: [Config]
|
||||
public var schemes: [Scheme]
|
||||
public var breakpoints: [Breakpoint]
|
||||
public var options: SpecOptions
|
||||
public var attributes: [String: Any]
|
||||
public var fileGroups: [String]
|
||||
@ -49,6 +50,7 @@ public struct Project: BuildSettingsContainer {
|
||||
settings: Settings = .empty,
|
||||
settingGroups: [String: Settings] = [:],
|
||||
schemes: [Scheme] = [],
|
||||
breakpoints: [Breakpoint] = [],
|
||||
packages: [String: SwiftPackage] = [:],
|
||||
options: SpecOptions = SpecOptions(),
|
||||
fileGroups: [String] = [],
|
||||
@ -66,6 +68,7 @@ public struct Project: BuildSettingsContainer {
|
||||
self.settings = settings
|
||||
self.settingGroups = settingGroups
|
||||
self.schemes = schemes
|
||||
self.breakpoints = breakpoints
|
||||
self.packages = packages
|
||||
self.options = options
|
||||
self.fileGroups = fileGroups
|
||||
@ -144,6 +147,7 @@ extension Project: Equatable {
|
||||
lhs.settingGroups == rhs.settingGroups &&
|
||||
lhs.configs == rhs.configs &&
|
||||
lhs.schemes == rhs.schemes &&
|
||||
lhs.breakpoints == rhs.breakpoints &&
|
||||
lhs.fileGroups == rhs.fileGroups &&
|
||||
lhs.configFiles == rhs.configFiles &&
|
||||
lhs.options == rhs.options &&
|
||||
@ -179,6 +183,11 @@ extension Project {
|
||||
aggregateTargets = try jsonDictionary.json(atKeyPath: "aggregateTargets").sorted { $0.name < $1.name }
|
||||
projectReferences = try jsonDictionary.json(atKeyPath: "projectReferences").sorted { $0.name < $1.name }
|
||||
schemes = try jsonDictionary.json(atKeyPath: "schemes")
|
||||
if jsonDictionary["breakpoints"] != nil {
|
||||
breakpoints = try jsonDictionary.json(atKeyPath: "breakpoints", invalidItemBehaviour: .fail)
|
||||
} else {
|
||||
breakpoints = []
|
||||
}
|
||||
fileGroups = jsonDictionary.json(atKeyPath: "fileGroups") ?? []
|
||||
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
|
||||
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
|
||||
|
@ -8,6 +8,12 @@ public enum SpecParsingError: Error, CustomStringConvertible {
|
||||
case invalidSourceBuildPhase(String)
|
||||
case invalidTargetReference(String)
|
||||
case invalidVersion(String)
|
||||
case unknownBreakpointType(String)
|
||||
case unknownBreakpointScope(String)
|
||||
case unknownBreakpointStopOnStyle(String)
|
||||
case unknownBreakpointActionType(String)
|
||||
case unknownBreakpointActionConveyanceType(String)
|
||||
case unknownBreakpointActionSoundName(String)
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
@ -25,6 +31,18 @@ public enum SpecParsingError: Error, CustomStringConvertible {
|
||||
return "Invalid version: \(version)"
|
||||
case let .unknownPackageRequirement(package):
|
||||
return "Unknown package requirement: \(package)"
|
||||
case let .unknownBreakpointType(type):
|
||||
return "Unknown Breakpoint type: \(type)"
|
||||
case let .unknownBreakpointScope(scope):
|
||||
return "Unknown Breakpoint scope: \(scope)"
|
||||
case let .unknownBreakpointStopOnStyle(stopOnStyle):
|
||||
return "Unknown Breakpoint stopOnStyle: \(stopOnStyle)"
|
||||
case let .unknownBreakpointActionType(type):
|
||||
return "Unknown Breakpoint Action type: \(type)"
|
||||
case let .unknownBreakpointActionConveyanceType(type):
|
||||
return "Unknown Breakpoint Action conveyance type: \(type)"
|
||||
case let .unknownBreakpointActionSoundName(name):
|
||||
return "Unknown Breakpoint Action sound name: \(name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,3 +106,30 @@ extension XCScheme.CommandLineArguments {
|
||||
self.init(arguments: args)
|
||||
}
|
||||
}
|
||||
|
||||
extension BreakpointExtensionID {
|
||||
|
||||
init(string: String) throws {
|
||||
if let id = BreakpointExtensionID(rawValue: "Xcode.Breakpoint.\(string)Breakpoint") {
|
||||
self = id
|
||||
} else if let id = BreakpointExtensionID(rawValue: string) {
|
||||
self = id
|
||||
} else {
|
||||
throw SpecParsingError.unknownBreakpointType(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension BreakpointActionExtensionID {
|
||||
|
||||
init(string: String) throws {
|
||||
if let type = BreakpointActionExtensionID(rawValue: "Xcode.BreakpointAction.\(string)") {
|
||||
self = type
|
||||
} else if let type = BreakpointActionExtensionID(rawValue: string) {
|
||||
self = type
|
||||
} else {
|
||||
throw SpecParsingError.unknownBreakpointActionType(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
124
Sources/XcodeGenKit/BreakpointGenerator.swift
Normal file
124
Sources/XcodeGenKit/BreakpointGenerator.swift
Normal file
@ -0,0 +1,124 @@
|
||||
import Foundation
|
||||
import ProjectSpec
|
||||
import XcodeProj
|
||||
|
||||
public class BreakpointGenerator {
|
||||
|
||||
let project: Project
|
||||
|
||||
public init(project: Project) {
|
||||
self.project = project
|
||||
}
|
||||
|
||||
func generateBreakpointList() throws -> XCBreakpointList? {
|
||||
let breakpoints = project.breakpoints
|
||||
guard !breakpoints.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return XCBreakpointList(type: "4", version: "2.0", breakpoints: try breakpoints.map({ try generateBreakpointProxy($0) }))
|
||||
}
|
||||
|
||||
private func generateBreakpointProxy(_ breakpoint: Breakpoint) throws -> XCBreakpointList.BreakpointProxy {
|
||||
let breakpointExtensionID: BreakpointExtensionID
|
||||
var filePath: String?
|
||||
var line: String?
|
||||
var column: String?
|
||||
var scope: String?
|
||||
var stopOnStyle: String?
|
||||
var symbol: String?
|
||||
var module: String?
|
||||
switch breakpoint.type {
|
||||
case let .file(path, lineNumber, columnNumber):
|
||||
breakpointExtensionID = .file
|
||||
filePath = path
|
||||
line = String(lineNumber)
|
||||
column = columnNumber.map(String.init)
|
||||
case let .exception(exception):
|
||||
breakpointExtensionID = .exception
|
||||
scope = exception.scope.rawValue
|
||||
stopOnStyle = exception.stopOnStyle.rawValue
|
||||
case .swiftError:
|
||||
breakpointExtensionID = .swiftError
|
||||
case .openGLError:
|
||||
breakpointExtensionID = .openGLError
|
||||
case let .symbolic(symbolName, moduleName):
|
||||
breakpointExtensionID = .symbolic
|
||||
symbol = symbolName
|
||||
module = moduleName
|
||||
case .ideConstraintError:
|
||||
breakpointExtensionID = .ideConstraintError
|
||||
case .ideTestFailure:
|
||||
breakpointExtensionID = .ideTestFailure
|
||||
}
|
||||
let xcbreakpoint = XCBreakpointList.BreakpointProxy.BreakpointContent(
|
||||
enabled: breakpoint.enabled,
|
||||
ignoreCount: String(breakpoint.ignoreCount),
|
||||
continueAfterRunningActions: breakpoint.continueAfterRunningActions,
|
||||
filePath: filePath,
|
||||
startingColumn: column,
|
||||
endingColumn: column,
|
||||
startingLine: line,
|
||||
endingLine: line,
|
||||
symbol: symbol,
|
||||
module: module,
|
||||
scope: scope,
|
||||
stopOnStyle: stopOnStyle,
|
||||
condition: breakpoint.condition,
|
||||
actions: try breakpoint.actions.map { try generateBreakpointActionProxy($0) }
|
||||
)
|
||||
|
||||
return XCBreakpointList.BreakpointProxy(
|
||||
breakpointExtensionID: breakpointExtensionID,
|
||||
breakpointContent: xcbreakpoint
|
||||
)
|
||||
}
|
||||
|
||||
private func generateBreakpointActionProxy(_ breakpointAction: Breakpoint.Action) throws -> XCBreakpointList.BreakpointProxy.BreakpointContent.BreakpointActionProxy {
|
||||
let actionExtensionID: BreakpointActionExtensionID
|
||||
var consoleCommand: String?
|
||||
var message: String?
|
||||
var conveyanceType: String?
|
||||
var command: String?
|
||||
var arguments: String?
|
||||
var waitUntilDone: Bool?
|
||||
var script: String?
|
||||
var soundName: String?
|
||||
switch breakpointAction {
|
||||
case let .debuggerCommand(command):
|
||||
actionExtensionID = .debuggerCommand
|
||||
consoleCommand = command
|
||||
case let .log(log):
|
||||
actionExtensionID = .log
|
||||
message = log.message
|
||||
conveyanceType = log.conveyanceType.rawValue
|
||||
case let .shellCommand(commandPath, commandArguments, waitUntilCommandDone):
|
||||
actionExtensionID = .shellCommand
|
||||
command = commandPath
|
||||
arguments = commandArguments
|
||||
waitUntilDone = waitUntilCommandDone
|
||||
case .graphicsTrace:
|
||||
actionExtensionID = .graphicsTrace
|
||||
case let .appleScript(appleScript):
|
||||
actionExtensionID = .appleScript
|
||||
script = appleScript
|
||||
case let .sound(sound):
|
||||
actionExtensionID = .sound
|
||||
soundName = sound.rawValue
|
||||
}
|
||||
let xcaction = XCBreakpointList.BreakpointProxy.BreakpointContent.BreakpointActionProxy.ActionContent(
|
||||
consoleCommand: consoleCommand,
|
||||
message: message,
|
||||
conveyanceType: conveyanceType,
|
||||
command: command,
|
||||
arguments: arguments,
|
||||
waitUntilDone: waitUntilDone,
|
||||
script: script,
|
||||
soundName: soundName
|
||||
)
|
||||
|
||||
return XCBreakpointList.BreakpointProxy.BreakpointContent.BreakpointActionProxy(
|
||||
actionExtensionID: actionExtensionID,
|
||||
actionContent: xcaction
|
||||
)
|
||||
}
|
||||
}
|
@ -27,8 +27,12 @@ public class ProjectGenerator {
|
||||
let schemeGenerator = SchemeGenerator(project: project, pbxProj: pbxProj)
|
||||
let (sharedSchemes, userSchemes, schemeManagement) = try schemeGenerator.generateSchemes()
|
||||
|
||||
// generate Breakpoints
|
||||
let breakpointGenerator = BreakpointGenerator(project: project)
|
||||
let xcbreakpointlist = try breakpointGenerator.generateBreakpointList()
|
||||
|
||||
// generate shared data
|
||||
let sharedData = XCSharedData(schemes: sharedSchemes)
|
||||
let sharedData = XCSharedData(schemes: sharedSchemes, breakpoints: xcbreakpointlist)
|
||||
|
||||
// generate user data
|
||||
let userData = userSchemes.isEmpty && schemeManagement == nil ? [] : [
|
||||
|
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "4"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
condition = "launchOptions == nil"
|
||||
endingLineNumber = "7"
|
||||
filePath = "App_iOS/AppDelegate.swift"
|
||||
startingLineNumber = "7">
|
||||
<Actions>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.Log">
|
||||
<ActionContent
|
||||
message = "message"
|
||||
conveyanceType = "1">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
endingColumnNumber = "13"
|
||||
endingLineNumber = "11"
|
||||
filePath = "App_iOS/AppDelegate.swift"
|
||||
startingColumnNumber = "13"
|
||||
startingLineNumber = "11">
|
||||
<Actions>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
scope = "0"
|
||||
stopOnStyle = "0">
|
||||
<Actions>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.DebuggerCommand">
|
||||
<ActionContent
|
||||
consoleCommand = "po $arg1">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.AppleScript">
|
||||
<ActionContent
|
||||
script = "display alert "Exception happened!"">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.Sound">
|
||||
<ActionContent
|
||||
soundName = "Blow">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No">
|
||||
<Actions>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.OpenGLErrorBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "2"
|
||||
continueAfterRunningActions = "No">
|
||||
<Actions>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.ShellCommand">
|
||||
<ActionContent
|
||||
arguments = "argument1, argument2"
|
||||
command = "script.sh"
|
||||
waitUntilDone = "YES">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SymbolicBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "UIViewAlertForUnsatisfiableConstraints"
|
||||
moduleName = "UIKitCore">
|
||||
<Actions>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.GraphicsTrace">
|
||||
<ActionContent>
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.IDEConstraintErrorBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "Yes">
|
||||
<Actions>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.IDETestFailureBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No">
|
||||
<Actions>
|
||||
</Actions>
|
||||
<Locations>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
@ -31,6 +31,46 @@ packages:
|
||||
Swinject:
|
||||
url: https://github.com/Swinject/Swinject
|
||||
version: 2.8.0
|
||||
breakpoints:
|
||||
- type: File
|
||||
path: App_iOS/AppDelegate.swift
|
||||
line: 7
|
||||
condition: launchOptions == nil
|
||||
actions:
|
||||
- type: Log
|
||||
message: message
|
||||
conveyanceType: speak
|
||||
- type: File
|
||||
path: App_iOS/AppDelegate.swift
|
||||
line: 11
|
||||
column: 13
|
||||
- type: Exception
|
||||
scope: All
|
||||
stopOnStype: Catch
|
||||
actions:
|
||||
- type: DebuggerCommand
|
||||
command: po $arg1
|
||||
- type: AppleScript
|
||||
script: display alert "Exception happened!"
|
||||
- type: Sound
|
||||
sound: Blow
|
||||
- type: SwiftError
|
||||
enabled: false
|
||||
- type: OpenGLError
|
||||
ignoreCount: 2
|
||||
actions:
|
||||
- type: ShellCommand
|
||||
path: script.sh
|
||||
arguments: argument1, argument2
|
||||
waitUntilDone: true
|
||||
- type: Symbolic
|
||||
symbol: UIViewAlertForUnsatisfiableConstraints
|
||||
module: UIKitCore
|
||||
actions:
|
||||
- type: GraphicsTrace
|
||||
- type: IDEConstraintError
|
||||
continueAfterRunningActions: true
|
||||
- type: IDETestFailure
|
||||
targets:
|
||||
Legacy:
|
||||
type: ""
|
||||
|
@ -379,6 +379,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
|
||||
func testProjectSpecParser() {
|
||||
let validTarget: [String: Any] = ["type": "application", "platform": "iOS"]
|
||||
let validBreakpoint: [String: Any] = ["type": "Exception", "scope": "All", "stopOnStyle": "Catch"]
|
||||
let invalid = "invalid"
|
||||
|
||||
describe {
|
||||
@ -401,6 +402,93 @@ class SpecLoadingTests: XCTestCase {
|
||||
try expectTargetError(target, .invalidDependency([invalid: "name"]))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint type") {
|
||||
var breakpoint = validBreakpoint
|
||||
breakpoint["type"] = invalid
|
||||
try expectBreakpointError(breakpoint, .unknownBreakpointType(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint scope") {
|
||||
var target = validBreakpoint
|
||||
target["scope"] = invalid
|
||||
try expectBreakpointError(target, .unknownBreakpointScope(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint stop on style") {
|
||||
var target = validBreakpoint
|
||||
target["stopOnStyle"] = invalid
|
||||
try expectBreakpointError(target, .unknownBreakpointStopOnStyle(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint action type") {
|
||||
var breakpoint = validBreakpoint
|
||||
breakpoint["actions"] = [["type": invalid]]
|
||||
try expectBreakpointError(breakpoint, .unknownBreakpointActionType(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint action conveyance type") {
|
||||
var breakpoint = validBreakpoint
|
||||
breakpoint["actions"] = [["type": "Log", "conveyanceType": invalid]]
|
||||
try expectBreakpointError(breakpoint, .unknownBreakpointActionConveyanceType(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect breakpoint action sound name") {
|
||||
var breakpoint = validBreakpoint
|
||||
breakpoint["actions"] = [["type": "Sound", "sound": invalid]]
|
||||
try expectBreakpointError(breakpoint, .unknownBreakpointActionSoundName(invalid))
|
||||
}
|
||||
|
||||
$0.it("parses breakpoints") {
|
||||
let breakpointDictionaries = [
|
||||
["type": "File", "path": "Foo.swift", "line": 7, "column": 14, "condition": "bar == nil"],
|
||||
["type": "Exception", "scope": "All", "stopOnStyle": "Catch"],
|
||||
["type": "SwiftError", "enabled": false],
|
||||
["type": "OpenGLError", "ignoreCount": 2],
|
||||
["type": "Symbolic", "symbol": "UIViewAlertForUnsatisfiableConstraints", "module": "UIKitCore"],
|
||||
["type": "IDEConstraintError", "continueAfterRunningActions": true],
|
||||
["type": "IDETestFailure"],
|
||||
]
|
||||
|
||||
let project = try getProjectSpec(["breakpoints": breakpointDictionaries])
|
||||
|
||||
let expectedBreakpoints = [
|
||||
Breakpoint(type: .file(path: "Foo.swift", line: 7, column: 14), condition: "bar == nil"),
|
||||
Breakpoint(type: .exception(.init(scope: .all, stopOnStyle: .catch))),
|
||||
Breakpoint(type: .swiftError, enabled: false),
|
||||
Breakpoint(type: .openGLError, ignoreCount: 2),
|
||||
Breakpoint(type: .symbolic(symbol: "UIViewAlertForUnsatisfiableConstraints", module: "UIKitCore")),
|
||||
Breakpoint(type: .ideConstraintError, continueAfterRunningActions: true),
|
||||
Breakpoint(type: .ideTestFailure),
|
||||
]
|
||||
|
||||
try expect(project.breakpoints) == expectedBreakpoints
|
||||
}
|
||||
|
||||
$0.it("parses breakpoint actions") {
|
||||
var breakpointDicationary = validBreakpoint
|
||||
breakpointDicationary["actions"] = [
|
||||
["type": "DebuggerCommand", "command": "po $arg1"],
|
||||
["type": "Log", "message": "message", "conveyanceType": "speak"],
|
||||
["type": "ShellCommand", "path": "script.sh", "arguments": "argument1, argument2", "waitUntilDone": true],
|
||||
["type": "GraphicsTrace"],
|
||||
["type": "AppleScript", "script": #"display alert "Hello!""#],
|
||||
["type": "Sound", "sound": "Hero"],
|
||||
]
|
||||
|
||||
let breakpoint = try Breakpoint(jsonDictionary: breakpointDicationary)
|
||||
|
||||
let expectedActions: [Breakpoint.Action] = [
|
||||
.debuggerCommand("po $arg1"),
|
||||
.log(.init(message: "message", conveyanceType: .speak)),
|
||||
.shellCommand(path: "script.sh", arguments: "argument1, argument2", waitUntilDone: true),
|
||||
.graphicsTrace,
|
||||
.appleScript(#"display alert "Hello!""#),
|
||||
.sound(.hero),
|
||||
]
|
||||
|
||||
try expect(breakpoint.actions) == expectedActions
|
||||
}
|
||||
|
||||
$0.it("parses sources") {
|
||||
var targetDictionary1 = validTarget
|
||||
targetDictionary1["sources"] = [
|
||||
@ -1482,3 +1570,9 @@ private func expectTargetError(_ target: [String: Any], _ expectedError: SpecPar
|
||||
_ = try Target(name: "test", jsonDictionary: target)
|
||||
}
|
||||
}
|
||||
|
||||
private func expectBreakpointError(_ breakpoint: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
|
||||
try expectError(expectedError, file: file, line: line) {
|
||||
_ = try Breakpoint(jsonDictionary: breakpoint)
|
||||
}
|
||||
}
|
||||
|
20
Tests/XcodeGenKitTests/BreakpointGeneratorTests.swift
Normal file
20
Tests/XcodeGenKitTests/BreakpointGeneratorTests.swift
Normal file
@ -0,0 +1,20 @@
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import TestSupport
|
||||
import XCTest
|
||||
|
||||
class BreakpointGeneratorTests: XCTestCase {
|
||||
|
||||
func testBreakpoints() {
|
||||
describe {
|
||||
|
||||
$0.it("generates breakpoint") {
|
||||
let breakpoint = Breakpoint(type: .exception(.init()))
|
||||
let project = Project(basePath: "", name: "test", targets: [], breakpoints: [breakpoint])
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
let xcbreakpoint = try unwrap(xcodeProject.sharedData?.breakpoints?.breakpoints.first)
|
||||
try expect(xcbreakpoint.breakpointExtensionID.rawValue) == "Xcode.Breakpoint.ExceptionBreakpoint"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user