From 8a993fd94c6febb755b63c3f0a2c05cc289ba331 Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Sun, 24 Sep 2017 19:42:14 +0200 Subject: [PATCH] move spec validation into seperate file --- Sources/XcodeGenKit/ProjectGenerator.swift | 154 +------------------ Sources/XcodeGenKit/SpecValidation.swift | 165 +++++++++++++++++++++ 2 files changed, 166 insertions(+), 153 deletions(-) create mode 100644 Sources/XcodeGenKit/SpecValidation.swift diff --git a/Sources/XcodeGenKit/ProjectGenerator.swift b/Sources/XcodeGenKit/ProjectGenerator.swift index d5a8c5a3..cef17df1 100644 --- a/Sources/XcodeGenKit/ProjectGenerator.swift +++ b/Sources/XcodeGenKit/ProjectGenerator.swift @@ -32,117 +32,8 @@ public class ProjectGenerator { return spec.configs.first { $0.type == .release }! } - public func validate() throws { - - if spec.configs.isEmpty { - spec.configs = [Config(name: "Debug", type: .debug), Config(name: "Release", type: .release)] - } - - var errors: [SpecValidationError.Error] = [] - - func validateSettings(_ settings: Settings) -> [SpecValidationError.Error] { - var errors: [SpecValidationError.Error] = [] - for preset in settings.groups { - if let settings = spec.settingGroups[preset] { - errors += validateSettings(settings) - } else { - errors.append(.invalidSettingsPreset(preset)) - } - } - return errors - } - - for settings in spec.settingGroups.values { - errors += validateSettings(settings) - } - - for target in spec.targets { - for dependency in target.dependencies { - if dependency.type == .target, spec.getTarget(dependency.reference) == nil { - errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) - } - } - - for (config, configFile) in target.configFiles { - if !(path + configFile).exists { - errors.append(.invalidTargetConfigFile(configFile: configFile, config: config, target: target.name)) - } - } - - for config in target.settings.configSettings.keys { - if spec.getConfig(config) == nil { - errors.append(.invalidBuildSettingConfig(config)) - } - } - - for source in target.sources { - let sourcePath = path + source - if !sourcePath.exists { - errors.append(.missingTargetSource(target: target.name, source: sourcePath.string)) - } - } - - if let scheme = target.scheme { - - for configVariant in scheme.configVariants { - if !spec.configs.contains(where: { $0.name.contains(configVariant) && $0.type == .debug }) { - errors.append(.invalidTargetSchemeConfigVariant(target: target.name, configVariant: configVariant, configType: .debug)) - } - if !spec.configs.contains(where: { $0.name.contains(configVariant) && $0.type == .release }) { - errors.append(.invalidTargetSchemeConfigVariant(target: target.name, configVariant: configVariant, configType: .release)) - } - } - - for testTarget in scheme.testTargets { - if spec.getTarget(testTarget) == nil { - errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget)) - } - } - } - - let scripts = target.prebuildScripts + target.postbuildScripts - for script in scripts { - if case let .path(pathString) = script.script { - let scriptPath = path + pathString - if !scriptPath.exists { - errors.append(.invalidBuildScriptPath(target: target.name, path: pathString)) - } - } - } - - errors += validateSettings(target.settings) - } - - for scheme in spec.schemes { - for buildTarget in scheme.build.targets { - if spec.getTarget(buildTarget.target) == nil { - errors.append(.invalidSchemeTarget(scheme: scheme.name, target: buildTarget.target)) - } - } - if let buildAction = scheme.run, spec.getConfig(buildAction.config) == nil { - errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) - } - if let buildAction = scheme.test, spec.getConfig(buildAction.config) == nil { - errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) - } - if let buildAction = scheme.profile, spec.getConfig(buildAction.config) == nil { - errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) - } - if let buildAction = scheme.analyze, spec.getConfig(buildAction.config) == nil { - errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) - } - if let buildAction = scheme.archive, spec.getConfig(buildAction.config) == nil { - errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) - } - } - - if !errors.isEmpty { - throw SpecValidationError(errors: errors) - } - } - public func generateProject() throws -> XcodeProj { - try validate() + try spec.validate(path: path) let pbxProjGenerator = PBXProjGenerator(spec: spec, path: path, currentXcodeVersion: currentXcodeVersion) let pbxProject = try pbxProjGenerator.generate() let workspace = try generateWorkspace() @@ -246,46 +137,3 @@ public class ProjectGenerator { return XCSharedData(schemes: xcschemes) } } - -public struct SpecValidationError: Error, CustomStringConvertible { - - public var errors: [Error] - - public enum Error: CustomStringConvertible { - case invalidTargetDependency(target: String, dependency: String) - case invalidSchemeTarget(scheme: String, target: String) - case invalidSchemeConfig(scheme: String, config: String) - case invalidTargetConfigFile(configFile: String, config: String, target: String) - case invalidBuildSettingConfig(String) - case invalidSettingsPreset(String) - case missingTargetSource(target: String, source: String) - case invalidBuildScriptPath(target: String, path: String) - case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType) - case invalidTargetSchemeTest(target: String, testTarget: String) - - public var description: String { - switch self { - case let .invalidTargetDependency(target, dependency): return "Target \(target.quoted) has invalid dependency: \(dependency.quoted)" - case let .invalidTargetConfigFile(configFile, config, target): return "Target \(target.quoted) has invalid config file \(configFile.quoted) for config \(config.quoted)" - case let .invalidSchemeTarget(scheme, target): return "Scheme \(scheme.quoted) has invalid build target \(target.quoted)" - case let .invalidSchemeConfig(scheme, config): return "Scheme \(scheme.quoted) has invalid build configuration \(config.quoted)" - case let .invalidBuildSettingConfig(config): return "Build setting has invalid build configuration \(config.quoted)" - case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)" - case let .invalidSettingsPreset(preset): return "Invalid settings preset \(preset.quoted)" - case let .invalidBuildScriptPath(target, path): return "Target \(target.quoted) has a script path that doesn't exist \(path.quoted)" - case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has invalid scheme config varians which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)" - case let .invalidTargetSchemeTest(target, test): return "Target \(target.quoted) scheme has invalid test \(test.quoted)" - } - } - } - - public var description: String { - let title: String - if errors.count == 1 { - title = "Spec validation error: " - } else { - title = "\(errors.count) Spec validations errors:\n\t- " - } - return "\(title)" + errors.map { $0.description }.joined(separator: "\n\t- ") - } -} diff --git a/Sources/XcodeGenKit/SpecValidation.swift b/Sources/XcodeGenKit/SpecValidation.swift new file mode 100644 index 00000000..ab2743c5 --- /dev/null +++ b/Sources/XcodeGenKit/SpecValidation.swift @@ -0,0 +1,165 @@ +// +// SpecValidation.swift +// XcodeGenKit +// +// Created by Yonas Kolb on 24/9/17. +// + +import Foundation +import ProjectSpec +import PathKit + +extension ProjectSpec { + + public mutating func validate(path: Path) throws { + + if configs.isEmpty { + configs = [Config(name: "Debug", type: .debug), Config(name: "Release", type: .release)] + } + + var errors: [SpecValidationError.Error] = [] + + func validateSettings(_ settings: Settings) -> [SpecValidationError.Error] { + var errors: [SpecValidationError.Error] = [] + for preset in settings.groups { + if let settings = settingGroups[preset] { + errors += validateSettings(settings) + } else { + errors.append(.invalidSettingsPreset(preset)) + } + } + return errors + } + + for settings in settingGroups.values { + errors += validateSettings(settings) + } + + for target in targets { + for dependency in target.dependencies { + if dependency.type == .target, getTarget(dependency.reference) == nil { + errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) + } + } + + for (config, configFile) in target.configFiles { + if !(path + configFile).exists { + errors.append(.invalidTargetConfigFile(configFile: configFile, config: config, target: target.name)) + } + } + + for config in target.settings.configSettings.keys { + if getConfig(config) == nil { + errors.append(.invalidBuildSettingConfig(config)) + } + } + + for source in target.sources { + let sourcePath = path + source + if !sourcePath.exists { + errors.append(.missingTargetSource(target: target.name, source: sourcePath.string)) + } + } + + if let scheme = target.scheme { + + for configVariant in scheme.configVariants { + if !configs.contains(where: { $0.name.contains(configVariant) && $0.type == .debug }) { + errors.append(.invalidTargetSchemeConfigVariant(target: target.name, configVariant: configVariant, configType: .debug)) + } + if !configs.contains(where: { $0.name.contains(configVariant) && $0.type == .release }) { + errors.append(.invalidTargetSchemeConfigVariant(target: target.name, configVariant: configVariant, configType: .release)) + } + } + + for testTarget in scheme.testTargets { + if getTarget(testTarget) == nil { + errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget)) + } + } + } + + let scripts = target.prebuildScripts + target.postbuildScripts + for script in scripts { + if case let .path(pathString) = script.script { + let scriptPath = path + pathString + if !scriptPath.exists { + errors.append(.invalidBuildScriptPath(target: target.name, path: pathString)) + } + } + } + + errors += validateSettings(target.settings) + } + + for scheme in schemes { + for buildTarget in scheme.build.targets { + if getTarget(buildTarget.target) == nil { + errors.append(.invalidSchemeTarget(scheme: scheme.name, target: buildTarget.target)) + } + } + if let buildAction = scheme.run, getConfig(buildAction.config) == nil { + errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) + } + if let buildAction = scheme.test, getConfig(buildAction.config) == nil { + errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) + } + if let buildAction = scheme.profile, getConfig(buildAction.config) == nil { + errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) + } + if let buildAction = scheme.analyze, getConfig(buildAction.config) == nil { + errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) + } + if let buildAction = scheme.archive, getConfig(buildAction.config) == nil { + errors.append(.invalidSchemeConfig(scheme: scheme.name, config: buildAction.config)) + } + } + + if !errors.isEmpty { + throw SpecValidationError(errors: errors) + } + } +} + +public struct SpecValidationError: Error, CustomStringConvertible { + + public var errors: [Error] + + public enum Error: CustomStringConvertible { + case invalidTargetDependency(target: String, dependency: String) + case invalidSchemeTarget(scheme: String, target: String) + case invalidSchemeConfig(scheme: String, config: String) + case invalidTargetConfigFile(configFile: String, config: String, target: String) + case invalidBuildSettingConfig(String) + case invalidSettingsPreset(String) + case missingTargetSource(target: String, source: String) + case invalidBuildScriptPath(target: String, path: String) + case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType) + case invalidTargetSchemeTest(target: String, testTarget: String) + + public var description: String { + switch self { + case let .invalidTargetDependency(target, dependency): return "Target \(target.quoted) has invalid dependency: \(dependency.quoted)" + case let .invalidTargetConfigFile(configFile, config, target): return "Target \(target.quoted) has invalid config file \(configFile.quoted) for config \(config.quoted)" + case let .invalidSchemeTarget(scheme, target): return "Scheme \(scheme.quoted) has invalid build target \(target.quoted)" + case let .invalidSchemeConfig(scheme, config): return "Scheme \(scheme.quoted) has invalid build configuration \(config.quoted)" + case let .invalidBuildSettingConfig(config): return "Build setting has invalid build configuration \(config.quoted)" + case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)" + case let .invalidSettingsPreset(preset): return "Invalid settings preset \(preset.quoted)" + case let .invalidBuildScriptPath(target, path): return "Target \(target.quoted) has a script path that doesn't exist \(path.quoted)" + case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has invalid scheme config varians which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)" + case let .invalidTargetSchemeTest(target, test): return "Target \(target.quoted) scheme has invalid test \(test.quoted)" + } + } + } + + public var description: String { + let title: String + if errors.count == 1 { + title = "Spec validation error: " + } else { + title = "\(errors.count) Spec validations errors:\n\t- " + } + return "\(title)" + errors.map { $0.description }.joined(separator: "\n\t- ") + } +}