Merge pull request #545 from ryohey/projectspec-json

Add ability to encode ProjectSpec to JSON
This commit is contained in:
ryohey
2019-04-22 11:14:14 +09:00
committed by GitHub
16 changed files with 791 additions and 40 deletions
+4
View File
@@ -2,6 +2,10 @@
## Next Version
#### Added
- Added ability to encode ProjectSpec to JSON [#545](https://github.com/yonaskolb/XcodeGen/pull/545) @ryohey
## 2.5.0
#### Added
+13
View File
@@ -62,6 +62,19 @@ extension AggregateTarget: NamedJSONDictionaryConvertible {
}
}
extension AggregateTarget: JSONEncodable {
public func toJSONValue() -> Any {
return [
"settings": settings.toJSONValue(),
"targets": targets,
"configFiles": configFiles,
"attributes": attributes,
"buildScripts": buildScripts.map { $0.toJSONValue() },
"scheme": scheme?.toJSONValue()
] as [String: Any?]
}
}
extension AggregateTarget: PathContainer {
static var pathProperties: [PathProperty] {
+26
View File
@@ -80,3 +80,29 @@ extension BuildRule: JSONObjectConvertible {
name = jsonDictionary.json(atKeyPath: "name")
}
}
extension BuildRule: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"outputFiles": outputFiles,
"outputFilesCompilerFlags": outputFilesCompilerFlags,
"name": name
]
switch fileType {
case .pattern(let string):
dict["filePattern"] = string
case .type(let string):
dict["fileType"] = string
}
switch action {
case .compilerSpec(let string):
dict["compilerSpec"] = string
case .script(let string):
dict["script"] = string
}
return dict
}
}
+32 -4
View File
@@ -2,6 +2,8 @@ import Foundation
import JSONUtilities
public struct BuildScript: Equatable {
public static let runOnlyWhenInstallingDefault = false
public static let showEnvVarsDefault = true
public var script: ScriptType
public var name: String?
@@ -26,8 +28,8 @@ public struct BuildScript: Equatable {
inputFileLists: [String] = [],
outputFileLists: [String] = [],
shell: String? = nil,
runOnlyWhenInstalling: Bool = false,
showEnvVars: Bool = true
runOnlyWhenInstalling: Bool = runOnlyWhenInstallingDefault,
showEnvVars: Bool = showEnvVarsDefault
) {
self.script = script
self.name = name
@@ -57,8 +59,34 @@ extension BuildScript: JSONObjectConvertible {
script = .path(path)
}
shell = jsonDictionary.json(atKeyPath: "shell")
runOnlyWhenInstalling = jsonDictionary.json(atKeyPath: "runOnlyWhenInstalling") ?? false
showEnvVars = jsonDictionary.json(atKeyPath: "showEnvVars") ?? true
runOnlyWhenInstalling = jsonDictionary.json(atKeyPath: "runOnlyWhenInstalling") ?? BuildScript.runOnlyWhenInstallingDefault
showEnvVars = jsonDictionary.json(atKeyPath: "showEnvVars") ?? BuildScript.showEnvVarsDefault
}
}
extension BuildScript: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"inputFiles": inputFiles,
"inputFileLists": inputFileLists,
"outputFiles": outputFiles,
"outputFileLists": outputFileLists,
"runOnlyWhenInstalling": runOnlyWhenInstalling,
"name": name,
"shell": shell
]
if showEnvVars != BuildScript.showEnvVarsDefault {
dict["showEnvVars"] = showEnvVars
}
switch script {
case .path(let string):
dict["path"] = string
case .script(let string):
dict["script"] = string
}
return dict
}
}
+44 -5
View File
@@ -2,15 +2,18 @@ import Foundation
import JSONUtilities
public struct Dependency: Equatable {
public static let removeHeadersDefault = true
public static let implicitDefault = false
public static let weakLinkDefault = false
public var type: DependencyType
public var reference: String
public var embed: Bool?
public var codeSign: Bool?
public var removeHeaders: Bool = true
public var removeHeaders: Bool = removeHeadersDefault
public var link: Bool?
public var implicit: Bool = false
public var weakLink: Bool = false
public var implicit: Bool = implicitDefault
public var weakLink: Bool = weakLinkDefault
public init(
type: DependencyType,
@@ -18,8 +21,8 @@ public struct Dependency: Equatable {
embed: Bool? = nil,
codeSign: Bool? = nil,
link: Bool? = nil,
implicit: Bool = false,
weakLink: Bool = false
implicit: Bool = implicitDefault,
weakLink: Bool = weakLinkDefault
) {
self.type = type
self.reference = reference
@@ -81,6 +84,42 @@ extension Dependency: JSONObjectConvertible {
}
}
extension Dependency: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"embed": embed,
"codeSign": codeSign,
"link": link
]
if removeHeaders != Dependency.removeHeadersDefault {
dict["removeHeaders"] = removeHeaders
}
if implicit != Dependency.implicitDefault {
dict["implicit"] = implicit
}
if weakLink != Dependency.weakLinkDefault {
dict["weak"] = weakLink
}
switch type {
case .target:
dict["target"] = reference
case .framework:
dict["framework"] = reference
case .carthage(let findFrameworks):
dict["carthage"] = reference
if let findFrameworks = findFrameworks {
dict["findFrameworks"] = findFrameworks
}
case .sdk:
dict["sdk"] = reference
}
return dict
}
}
extension Dependency: PathContainer {
static var pathProperties: [PathProperty] {
@@ -78,3 +78,14 @@ extension DeploymentTarget: JSONObjectConvertible {
macOS = try parseVersion("macOS")
}
}
extension DeploymentTarget: JSONEncodable {
public func toJSONValue() -> Any {
return [
"iOS": iOS?.string,
"tvOS": tvOS?.string,
"watchOS": watchOS?.string,
"macOS": macOS?.string,
]
}
}
+7
View File
@@ -0,0 +1,7 @@
import Foundation
import JSONUtilities
public protocol JSONEncodable {
// returns JSONDictionary or JSONArray or JSONRawType or nil
func toJSONValue() -> Any
}
+9
View File
@@ -25,6 +25,15 @@ extension Plist: JSONObjectConvertible {
}
}
extension Plist: JSONEncodable {
public func toJSONValue() -> Any {
return [
"path": path,
"properties": properties
]
}
}
extension Plist: PathContainer {
static var pathProperties: [PathProperty] {
+28
View File
@@ -230,3 +230,31 @@ extension BuildSettingsContainer {
return configFiles.values.map { Path($0) }
}
}
extension Project: JSONEncodable {
public func toJSONValue() -> Any {
return toJSONDictionary()
}
public func toJSONDictionary() -> JSONDictionary {
let targetPairs = targets.map { ($0.name, $0.toJSONValue()) }
let configsPairs = configs.map { ($0.name, $0.type?.rawValue) }
let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) }
let schemesPairs = schemes.map { ($0.name, $0.toJSONValue()) }
return [
"name": name,
"options": options.toJSONValue(),
"settings": settings.toJSONValue(),
"fileGroups": fileGroups,
"configFiles": configFiles,
"include": include,
"attributes": attributes,
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
"configs": Dictionary(uniqueKeysWithValues: configsPairs),
"aggregateTargets": Dictionary(uniqueKeysWithValues: aggregateTargetsPairs),
"schemes": Dictionary(uniqueKeysWithValues: schemesPairs),
"settingGroups": settingGroups.mapValues { $0.toJSONValue() }
]
}
}
+179 -13
View File
@@ -44,6 +44,9 @@ public struct Scheme: Equatable {
}
public struct Build: Equatable {
public static let parallelizeBuildDefault = true
public static let buildImplicitDependenciesDefault = true
public var targets: [BuildTarget]
public var parallelizeBuild: Bool
public var buildImplicitDependencies: Bool
@@ -51,8 +54,8 @@ public struct Scheme: Equatable {
public var postActions: [ExecutionAction]
public init(
targets: [BuildTarget],
parallelizeBuild: Bool = true,
buildImplicitDependencies: Bool = true,
parallelizeBuild: Bool = parallelizeBuildDefault,
buildImplicitDependencies: Bool = buildImplicitDependenciesDefault,
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
) {
@@ -86,6 +89,8 @@ public struct Scheme: Equatable {
}
public struct Test: BuildAction {
public static let gatherCoverageDataDefault = false
public var config: String?
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
@@ -95,14 +100,17 @@ public struct Scheme: Equatable {
public var environmentVariables: [XCScheme.EnvironmentVariable]
public struct TestTarget: Equatable, ExpressibleByStringLiteral {
public static let randomExecutionOrderDefault = false
public static let parallelizableDefault = false
public let name: String
public var randomExecutionOrder: Bool
public var parallelizable: Bool
public init(
name: String,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false
randomExecutionOrder: Bool = randomExecutionOrderDefault,
parallelizable: Bool = parallelizableDefault
) {
self.name = name
self.randomExecutionOrder = randomExecutionOrder
@@ -118,7 +126,7 @@ public struct Scheme: Equatable {
public init(
config: String,
gatherCoverageData: Bool = false,
gatherCoverageData: Bool = gatherCoverageDataDefault,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false,
commandLineArguments: [String: Bool] = [:],
@@ -174,6 +182,8 @@ public struct Scheme: Equatable {
}
public struct Archive: BuildAction {
public static let revealArchiveInOrganizerDefault = true
public var config: String?
public var customArchiveName: String?
public var revealArchiveInOrganizer: Bool
@@ -182,7 +192,7 @@ public struct Scheme: Equatable {
public init(
config: String,
customArchiveName: String? = nil,
revealArchiveInOrganizer: Bool = true,
revealArchiveInOrganizer: Bool = revealArchiveInOrganizerDefault,
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
) {
@@ -218,6 +228,16 @@ extension Scheme.ExecutionAction: JSONObjectConvertible {
}
}
extension Scheme.ExecutionAction: JSONEncodable {
public func toJSONValue() -> Any {
return [
"script": script,
"name": name,
"settingsTarget": settingsTarget
]
}
}
extension Scheme.Run: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@@ -229,11 +249,23 @@ extension Scheme.Run: JSONObjectConvertible {
}
}
extension Scheme.Run: JSONEncodable {
public func toJSONValue() -> Any {
return [
"commandLineArguments": commandLineArguments,
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Test: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
config = jsonDictionary.json(atKeyPath: "config")
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? Scheme.Test.gatherCoverageDataDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
@@ -254,12 +286,47 @@ extension Scheme.Test: JSONObjectConvertible {
}
}
extension Scheme.Test: JSONEncodable {
public func toJSONValue() -> Any {
return [
"gatherCoverageData": gatherCoverageData,
"commandLineArguments": commandLineArguments,
"targets": targets.map { $0.toJSONValue() },
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Test.TestTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? false
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? false
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? Scheme.Test.TestTarget.randomExecutionOrderDefault
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? Scheme.Test.TestTarget.parallelizableDefault
}
}
extension Scheme.Test.TestTarget: JSONEncodable {
public func toJSONValue() -> Any {
if !randomExecutionOrder && !parallelizable {
return name
}
var dict: JSONDictionary = [
"name": name
]
if randomExecutionOrder != Scheme.Test.TestTarget.randomExecutionOrderDefault {
dict["randomExecutionOrder"] = randomExecutionOrder
}
if parallelizable != Scheme.Test.TestTarget.parallelizableDefault {
dict["parallelizable"] = parallelizable
}
return dict
}
}
@@ -274,6 +341,18 @@ extension Scheme.Profile: JSONObjectConvertible {
}
}
extension Scheme.Profile: JSONEncodable {
public func toJSONValue() -> Any {
return [
"commandLineArguments": commandLineArguments,
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Analyze: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@@ -281,17 +360,42 @@ extension Scheme.Analyze: JSONObjectConvertible {
}
}
extension Scheme.Analyze: JSONEncodable {
public func toJSONValue() -> Any {
return [
"config": config
]
}
}
extension Scheme.Archive: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
config = jsonDictionary.json(atKeyPath: "config")
customArchiveName = jsonDictionary.json(atKeyPath: "customArchiveName")
revealArchiveInOrganizer = jsonDictionary.json(atKeyPath: "revealArchiveInOrganizer") ?? true
revealArchiveInOrganizer = jsonDictionary.json(atKeyPath: "revealArchiveInOrganizer") ?? Scheme.Archive.revealArchiveInOrganizerDefault
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
extension Scheme.Archive: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"config": config,
"customArchiveName": customArchiveName,
]
if revealArchiveInOrganizer != Scheme.Archive.revealArchiveInOrganizerDefault {
dict["revealArchiveInOrganizer"] = revealArchiveInOrganizer
}
return dict
}
}
extension Scheme: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
@@ -305,6 +409,19 @@ extension Scheme: NamedJSONDictionaryConvertible {
}
}
extension Scheme: JSONEncodable {
public func toJSONValue() -> Any {
return [
"build": build.toJSONValue(),
"run": run?.toJSONValue(),
"test": test?.toJSONValue(),
"analyze": analyze?.toJSONValue(),
"profile": profile?.toJSONValue(),
"archive": archive?.toJSONValue(),
] as [String: Any?]
}
}
extension Scheme.Build: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@@ -332,8 +449,29 @@ extension Scheme.Build: JSONObjectConvertible {
self.targets = targets.sorted { $0.target < $1.target }
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? true
buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? true
parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? Scheme.Build.parallelizeBuildDefault
buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? Scheme.Build.buildImplicitDependenciesDefault
}
}
extension Scheme.Build: JSONEncodable {
public func toJSONValue() -> Any {
let targetPairs = targets.map { ($0.target, $0.buildTypes.map { $0.toJSONValue() }) }
var dict: JSONDictionary = [
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
]
if parallelizeBuild != Scheme.Build.parallelizeBuildDefault {
dict["parallelizeBuild"] = parallelizeBuild
}
if buildImplicitDependencies != Scheme.Build.buildImplicitDependenciesDefault {
dict["buildImplicitDependencies"] = buildImplicitDependencies
}
return dict
}
}
@@ -357,7 +495,20 @@ extension BuildType: JSONPrimitiveConvertible {
}
}
extension BuildType: JSONEncodable {
public func toJSONValue() -> Any {
switch self {
case .testing: return "testing"
case .profiling: return "profiling"
case .running: return "running"
case .archiving: return "archiving"
case .analyzing: return "analyzing"
}
}
}
extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
public static let enabledDefault = true
private static func parseValue(_ value: Any) -> String {
if let bool = value as? Bool {
@@ -377,7 +528,7 @@ extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
value = try jsonDictionary.json(atKeyPath: "value")
}
let variable: String = try jsonDictionary.json(atKeyPath: "variable")
let enabled: Bool = jsonDictionary.json(atKeyPath: "isEnabled") ?? true
let enabled: Bool = jsonDictionary.json(atKeyPath: "isEnabled") ?? XCScheme.EnvironmentVariable.enabledDefault
self.init(variable: variable, value: value, enabled: enabled)
}
@@ -393,3 +544,18 @@ extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
}
}
}
extension XCScheme.EnvironmentVariable: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any] = [
"variable": variable,
"value": value
]
if enabled != XCScheme.EnvironmentVariable.enabledDefault {
dict["isEnabled"] = enabled
}
return dict
}
}
+13
View File
@@ -105,3 +105,16 @@ public func += (lhs: inout BuildSettings, rhs: BuildSettings?) {
guard let rhs = rhs else { return }
lhs.merge(rhs)
}
extension Settings: JSONEncodable {
public func toJSONValue() -> Any {
if groups.count > 0 || configSettings.count > 0 {
return [
"base": buildSettings,
"groups": groups,
"configs": configSettings.mapValues { $0.toJSONValue() }
]
}
return buildSettings
}
}
+54 -12
View File
@@ -2,6 +2,12 @@ import Foundation
import JSONUtilities
public struct SpecOptions: Equatable {
public static let settingPresetsDefault = SettingPresets.all
public static let createIntermediateGroupsDefault = false
public static let transitivelyLinkDependenciesDefault = false
public static let groupSortPositionDefault = GroupSortPosition.bottom
public static let generateEmptyDirectoriesDefault = false
public static let findCarthageFrameworksDefault = false
public var minimumXcodeGenVersion: Version?
public var carthageBuildPath: String?
@@ -62,9 +68,9 @@ public struct SpecOptions: Equatable {
minimumXcodeGenVersion: Version? = nil,
carthageBuildPath: String? = nil,
carthageExecutablePath: String? = nil,
createIntermediateGroups: Bool = false,
createIntermediateGroups: Bool = createIntermediateGroupsDefault,
bundleIdPrefix: String? = nil,
settingPresets: SettingPresets = .all,
settingPresets: SettingPresets = settingPresetsDefault,
developmentLanguage: String? = nil,
indentWidth: UInt? = nil,
tabWidth: UInt? = nil,
@@ -73,10 +79,10 @@ public struct SpecOptions: Equatable {
deploymentTarget: DeploymentTarget = .init(),
disabledValidations: [ValidationType] = [],
defaultConfig: String? = nil,
transitivelyLinkDependencies: Bool = false,
groupSortPosition: GroupSortPosition = .bottom,
generateEmptyDirectories: Bool = false,
findCarthageFrameworks: Bool = false
transitivelyLinkDependencies: Bool = transitivelyLinkDependenciesDefault,
groupSortPosition: GroupSortPosition = groupSortPositionDefault,
generateEmptyDirectories: Bool = generateEmptyDirectoriesDefault,
findCarthageFrameworks: Bool = findCarthageFrameworksDefault
) {
self.minimumXcodeGenVersion = minimumXcodeGenVersion
self.carthageBuildPath = carthageBuildPath
@@ -109,8 +115,8 @@ extension SpecOptions: JSONObjectConvertible {
carthageBuildPath = jsonDictionary.json(atKeyPath: "carthageBuildPath")
carthageExecutablePath = jsonDictionary.json(atKeyPath: "carthageExecutablePath")
bundleIdPrefix = jsonDictionary.json(atKeyPath: "bundleIdPrefix")
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? .all
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups") ?? false
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? SpecOptions.settingPresetsDefault
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups") ?? SpecOptions.createIntermediateGroupsDefault
developmentLanguage = jsonDictionary.json(atKeyPath: "developmentLanguage")
usesTabs = jsonDictionary.json(atKeyPath: "usesTabs")
xcodeVersion = jsonDictionary.json(atKeyPath: "xcodeVersion")
@@ -119,10 +125,46 @@ extension SpecOptions: JSONObjectConvertible {
deploymentTarget = jsonDictionary.json(atKeyPath: "deploymentTarget") ?? DeploymentTarget()
disabledValidations = jsonDictionary.json(atKeyPath: "disabledValidations") ?? []
defaultConfig = jsonDictionary.json(atKeyPath: "defaultConfig")
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies") ?? false
groupSortPosition = jsonDictionary.json(atKeyPath: "groupSortPosition") ?? .bottom
generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? false
findCarthageFrameworks = jsonDictionary.json(atKeyPath: "findCarthageFrameworks") ?? false
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies") ?? SpecOptions.transitivelyLinkDependenciesDefault
groupSortPosition = jsonDictionary.json(atKeyPath: "groupSortPosition") ?? SpecOptions.groupSortPositionDefault
generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? SpecOptions.generateEmptyDirectoriesDefault
findCarthageFrameworks = jsonDictionary.json(atKeyPath: "findCarthageFrameworks") ?? SpecOptions.findCarthageFrameworksDefault
}
}
extension SpecOptions: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"deploymentTarget": deploymentTarget.toJSONValue(),
"transitivelyLinkDependencies": transitivelyLinkDependencies,
"groupSortPosition": groupSortPosition.rawValue,
"disabledValidations": disabledValidations.map { $0.rawValue },
"minimumXcodeGenVersion": minimumXcodeGenVersion?.string,
"carthageBuildPath": carthageBuildPath,
"carthageExecutablePath": carthageExecutablePath,
"bundleIdPrefix": bundleIdPrefix,
"developmentLanguage": developmentLanguage,
"usesTabs": usesTabs,
"xcodeVersion": xcodeVersion,
"indentWidth": indentWidth.flatMap { Int($0) },
"tabWidth": tabWidth.flatMap { Int($0) },
"defaultConfig": defaultConfig,
]
if settingPresets != SpecOptions.settingPresetsDefault {
dict["settingPresets"] = settingPresets.rawValue
}
if createIntermediateGroups != SpecOptions.createIntermediateGroupsDefault {
dict["createIntermediateGroups"] = createIntermediateGroups
}
if generateEmptyDirectories != SpecOptions.generateEmptyDirectoriesDefault {
dict["generateEmptyDirectories"] = generateEmptyDirectories
}
if findCarthageFrameworks != SpecOptions.findCarthageFrameworksDefault {
dict["findCarthageFrameworks"] = findCarthageFrameworks
}
return dict
}
}
+53 -2
View File
@@ -3,6 +3,8 @@ import JSONUtilities
import xcodeproj
public struct LegacyTarget: Equatable {
public static let passSettingsDefault = false
public var toolPath: String
public var arguments: String?
public var passSettings: Bool
@@ -10,7 +12,7 @@ public struct LegacyTarget: Equatable {
public init(
toolPath: String,
passSettings: Bool = false,
passSettings: Bool = passSettingsDefault,
arguments: String? = nil,
workingDirectory: String? = nil
) {
@@ -267,11 +269,27 @@ extension LegacyTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
toolPath = try jsonDictionary.json(atKeyPath: "toolPath")
arguments = jsonDictionary.json(atKeyPath: "arguments")
passSettings = jsonDictionary.json(atKeyPath: "passSettings") ?? false
passSettings = jsonDictionary.json(atKeyPath: "passSettings") ?? LegacyTarget.passSettingsDefault
workingDirectory = jsonDictionary.json(atKeyPath: "workingDirectory")
}
}
extension LegacyTarget: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"toolPath": toolPath,
"arguments": arguments,
"workingDirectory": workingDirectory,
]
if passSettings != LegacyTarget.passSettingsDefault {
dict["passSettings"] = passSettings
}
return dict
}
}
extension Target: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
@@ -342,3 +360,36 @@ extension Target: NamedJSONDictionaryConvertible {
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
}
}
extension Target: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"type": type.name,
"platform": platform.rawValue,
"settings": settings.toJSONValue(),
"configFiles": configFiles,
"attributes": attributes,
"sources": sources.map { $0.toJSONValue() },
"dependencies": dependencies.map { $0.toJSONValue() },
"postCompileScripts": postCompileScripts.map{ $0.toJSONValue() },
"prebuildScripts": preBuildScripts.map{ $0.toJSONValue() },
"postbuildScripts": postBuildScripts.map{ $0.toJSONValue() },
"buildRules": buildRules.map{ $0.toJSONValue() },
"deploymentTarget": deploymentTarget?.deploymentTarget,
"info": info?.toJSONValue(),
"entitlements": entitlements?.toJSONValue(),
"transitivelyLinkDependencies": transitivelyLinkDependencies,
"directlyEmbedCarthageDependencies": directlyEmbedCarthageDependencies,
"requiresObjCLinking": requiresObjCLinking,
"scheme": scheme?.toJSONValue(),
"legacy": legacy?.toJSONValue(),
]
if productName != name {
dict["productName"] = productName
}
return dict
}
}
+23 -2
View File
@@ -3,6 +3,8 @@ import JSONUtilities
import xcodeproj
public struct TargetScheme: Equatable {
public static let gatherCoverageDataDefault = false
public var testTargets: [Scheme.Test.TestTarget]
public var configVariants: [String]
public var gatherCoverageData: Bool
@@ -14,7 +16,7 @@ public struct TargetScheme: Equatable {
public init(
testTargets: [Scheme.Test.TestTarget] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
gatherCoverageData: Bool = gatherCoverageDataDefault,
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = [],
preActions: [Scheme.ExecutionAction] = [],
@@ -47,10 +49,29 @@ extension TargetScheme: JSONObjectConvertible {
testTargets = []
}
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? TargetScheme.gatherCoverageDataDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
extension TargetScheme: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any] = [
"configVariants": configVariants,
"commandLineArguments": commandLineArguments,
"testTargets": testTargets.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
]
if gatherCoverageData != TargetScheme.gatherCoverageDataDefault {
dict["gatherCoverageData"] = gatherCoverageData
}
return dict
}
}
+53 -2
View File
@@ -4,6 +4,7 @@ import PathKit
import xcodeproj
public struct TargetSource: Equatable {
public static let optionalDefault = false
public var path: String
public var name: String?
@@ -123,7 +124,7 @@ public struct TargetSource: Equatable {
compilerFlags: [String] = [],
excludes: [String] = [],
type: SourceType? = nil,
optional: Bool = false,
optional: Bool = optionalDefault,
buildPhase: BuildPhase? = nil,
headerVisibility: HeaderVisibility? = nil,
createIntermediateGroups: Bool? = nil
@@ -169,7 +170,7 @@ extension TargetSource: JSONObjectConvertible {
headerVisibility = jsonDictionary.json(atKeyPath: "headerVisibility")
excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
type = jsonDictionary.json(atKeyPath: "type")
optional = jsonDictionary.json(atKeyPath: "optional") ?? false
optional = jsonDictionary.json(atKeyPath: "optional") ?? TargetSource.optionalDefault
if let string: String = jsonDictionary.json(atKeyPath: "buildPhase") {
buildPhase = try BuildPhase(string: string)
@@ -181,6 +182,32 @@ extension TargetSource: JSONObjectConvertible {
}
}
extension TargetSource: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"compilerFlags": compilerFlags,
"excludes": excludes,
"name": name,
"headerVisibility": headerVisibility?.rawValue,
"type": type?.rawValue,
"buildPhase": buildPhase?.toJSONValue(),
"createIntermediateGroups": createIntermediateGroups,
]
if optional != TargetSource.optionalDefault {
dict["optional"] = optional
}
if dict.count == 0 {
return path
}
dict["path"] = path
return dict
}
}
extension TargetSource.BuildPhase {
public init(string: String) throws {
@@ -204,6 +231,21 @@ extension TargetSource.BuildPhase: JSONObjectConvertible {
}
}
extension TargetSource.BuildPhase: JSONEncodable {
public func toJSONValue() -> Any {
switch self {
case .sources: return "sources"
case .headers: return "headers"
case .resources: return "resources"
case .copyFiles(let files): return ["copyFiles": files.toJSONValue()]
case .none: return "none"
case .frameworks: fatalError("invalid build phase")
case .runScript: fatalError("invalid build phase")
case .carbonResources: fatalError("invalid build phase")
}
}
}
extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@@ -213,6 +255,15 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible {
}
}
extension TargetSource.BuildPhase.CopyFilesSettings: JSONEncodable {
public func toJSONValue() -> Any {
return [
"destination": destination.rawValue,
"subpath": subpath
]
}
}
extension TargetSource: PathContainer {
static var pathProperties: [PathProperty] {
@@ -256,6 +256,248 @@ class ProjectSpecTests: XCTestCase {
}
}
}
func testJSONEncodable() {
describe {
$0.it("encodes to json") {
let proj = Project(basePath: Path.current,
name: "ToJson",
configs: [Config(name: "DevelopmentConfig", type: .debug), Config(name: "ProductionConfig", type: .release)],
targets: [Target(name: "App",
type: .application,
platform: .iOS,
productName: "App",
deploymentTarget: Version(major: 0, minor: 1, patch: 2),
settings: Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"]),
configFiles: ["foo": "bar"],
sources: [TargetSource(path: "Source",
name: "Source",
compilerFlags: ["-Werror"],
excludes: ["foo", "bar"],
type: .folder,
optional: true,
buildPhase: .resources,
headerVisibility: .private,
createIntermediateGroups: true)],
dependencies: [Dependency(type: .carthage(findFrameworks: true),
reference: "reference",
embed: true,
codeSign: true,
link: true,
implicit: true,
weakLink: true)],
info: Plist(path: "info.plist", attributes: ["foo": "bar"]),
entitlements: Plist(path: "entitlements.plist", attributes: ["foo": "bar"]),
transitivelyLinkDependencies: true,
directlyEmbedCarthageDependencies: true,
requiresObjCLinking: true,
preBuildScripts: [BuildScript(script: .script("pwd"),
name: "Foo script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
postCompileScripts: [BuildScript(script: .path("cmd.sh"),
name: "Bar script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
postBuildScripts: [BuildScript(script: .path("cmd.sh"),
name: "an another script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
buildRules: [BuildRule(fileType: .pattern("*.xcassets"),
action: .script("pre_process_swift.py"),
name: "My Build Rule",
outputFiles: ["$(SRCROOT)/Generated.swift"],
outputFilesCompilerFlags: ["foo"]),
BuildRule(fileType: .type("sourcecode.swift"),
action: .compilerSpec("com.apple.xcode.tools.swift.compiler"),
name: nil,
outputFiles: ["bar"],
outputFilesCompilerFlags: ["foo"]),],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(name: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
gatherCoverageData: true,
commandLineArguments: ["foo": true],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable",
value: "bar",
enabled: true)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
legacy: LegacyTarget(toolPath: "foo",
passSettings: true,
arguments: "bar",
workingDirectory: "foo"),
attributes: ["foo": "bar"])],
aggregateTargets: [AggregateTarget(name: "aggregate target",
targets: ["App"],
settings: Settings(buildSettings: ["buildSettings": "bar"],
configSettings: ["configSettings": Settings(buildSettings: [:],
configSettings: [:],
groups: [])],
groups: ["foo"]),
configFiles: ["configFiles": "bar"],
buildScripts: [BuildScript(script: .path("script"),
name: "foo",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: false)],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(name: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
gatherCoverageData: true,
commandLineArguments: ["foo": true],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable",
value: "bar",
enabled: true)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
attributes: ["foo": "bar"])],
settings: Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"]),
settingGroups: ["foo": Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"])],
schemes: [Scheme(name: "scheme",
build: Scheme.Build(targets: [Scheme.BuildTarget(target: "foo",
buildTypes: [.archiving, .analyzing])],
parallelizeBuild: false,
buildImplicitDependencies: false,
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
run: Scheme.Run(config: "run config",
commandLineArguments: ["foo": true],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
test: Scheme.Test(config: "Config",
gatherCoverageData: true,
randomExecutionOrder: false,
parallelizable: false,
commandLineArguments: ["foo": true],
targets: [Scheme.Test.TestTarget(name: "foo",
randomExecutionOrder: false,
parallelizable: false)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
profile: Scheme.Profile(config: "profile config",
commandLineArguments: ["foo": true],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
analyze: Scheme.Analyze(config: "analyze config"),
archive: Scheme.Archive(config: "archive config",
customArchiveName: "customArchiveName",
revealArchiveInOrganizer: true,
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]))],
options: SpecOptions(minimumXcodeGenVersion: Version(major: 3, minor: 4, patch: 5),
carthageBuildPath: "carthageBuildPath",
carthageExecutablePath: "carthageExecutablePath",
createIntermediateGroups: true,
bundleIdPrefix: "bundleIdPrefix",
settingPresets: .project,
developmentLanguage: "developmentLanguage",
indentWidth: 123,
tabWidth: 456,
usesTabs: true,
xcodeVersion: "xcodeVersion",
deploymentTarget: DeploymentTarget(iOS: Version(major: 1, minor: 2, patch: 3),
tvOS: nil,
watchOS: Version(major: 4, minor: 5, patch: 6),
macOS: nil),
disabledValidations: [.missingConfigFiles],
defaultConfig: "defaultConfig",
transitivelyLinkDependencies: true,
groupSortPosition: .top,
generateEmptyDirectories: true,
findCarthageFrameworks: false),
fileGroups: ["foo", "bar"],
configFiles: ["configFiles": "bar"],
attributes: ["attributes": "bar"])
let json = proj.toJSONDictionary()
let restoredProj = try Project(basePath: Path.current, jsonDictionary: json)
// Examin some properties to make debugging easier
try expect(proj.aggregateTargets) == restoredProj.aggregateTargets
try expect(proj.configFiles) == restoredProj.configFiles
try expect(proj.settings) == restoredProj.settings
try expect(proj.basePath) == restoredProj.basePath
try expect(proj.fileGroups) == restoredProj.fileGroups
try expect(proj.schemes) == restoredProj.schemes
try expect(proj.options) == restoredProj.options
try expect(proj.settingGroups) == restoredProj.settingGroups
try expect(proj.targets) == restoredProj.targets
try expect(proj) == restoredProj
}
}
}
}
fileprivate func expectValidationError(_ project: Project, _ expectedError: SpecValidationError.ValidationError, file: String = #file, line: Int = #line) throws {