mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
Merge remote-tracking branch 'upstream/master' into ts-no-overwriting-xcconfig-value
This commit is contained in:
@@ -65,7 +65,8 @@ public class PBXProjGenerator {
|
||||
project = PBXProj(objectVersion: 46, rootObject: generateUUID(PBXProject.self, spec.name))
|
||||
|
||||
for group in spec.fileGroups {
|
||||
_ = try getGroups(path: spec.basePath + group)
|
||||
// TODO: call a seperate function that only creates groups not source files
|
||||
_ = try getSources(sourceMetadata: Source(path: group), path: spec.basePath + group)
|
||||
}
|
||||
|
||||
let buildConfigs: [XCBuildConfiguration] = spec.configs.map { config in
|
||||
@@ -143,13 +144,17 @@ public class PBXProjGenerator {
|
||||
let buildFile: PBXBuildFile
|
||||
}
|
||||
|
||||
func generateSourceFile(path: Path) -> SourceFile {
|
||||
func generateSourceFile(sourceMetadata source: Source, path: Path) -> SourceFile {
|
||||
let fileReference = fileReferencesByPath[path]!
|
||||
var settings: [String: Any]?
|
||||
var settings: [String: Any] = [:]
|
||||
if getBuildPhaseForPath(path) == .headers {
|
||||
settings = ["ATTRIBUTES": ["Public"]]
|
||||
}
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference), fileRef: fileReference, settings: settings)
|
||||
if source.compilerFlags.count > 0 {
|
||||
settings["COMPILER_FLAGS"] = source.compilerFlags.joined(separator: " ")
|
||||
}
|
||||
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference), fileRef: fileReference, settings: settings.isEmpty ? nil : settings)
|
||||
return SourceFile(path: path, fileReference: fileReference, buildFile: buildFile)
|
||||
}
|
||||
|
||||
@@ -157,17 +162,20 @@ public class PBXProjGenerator {
|
||||
|
||||
let carthageDependencies = getAllCarthageDependencies(target: target)
|
||||
|
||||
let sourcePaths = target.sources.map { spec.basePath + $0 }
|
||||
var sourceFiles: [SourceFile] = []
|
||||
let sourceFiles = try getAllSourceFiles(sources: target.sources)
|
||||
|
||||
for source in sourcePaths {
|
||||
let sourceGroups = try getGroups(path: source)
|
||||
sourceFiles += sourceGroups.sourceFiles
|
||||
}
|
||||
|
||||
// find all Info.plist
|
||||
let infoPlists: [Path] = sourcePaths.reduce([]) {
|
||||
$0 + ((try? $1.recursiveChildren()) ?? []).filter { $0.lastComponent == "Info.plist" }
|
||||
// find all Info.plist files
|
||||
let infoPlists: [Path] = target.sources.map { spec.basePath + $0.path }.flatMap { (path) -> [Path] in
|
||||
if path.isFile {
|
||||
if path.lastComponent == "Info.plist" {
|
||||
return [path]
|
||||
}
|
||||
} else {
|
||||
if let children = try? path.recursiveChildren() {
|
||||
return children.filter { $0.lastComponent == "Info.plist" }
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
let configs: [XCBuildConfiguration] = spec.configs.map { config in
|
||||
@@ -354,9 +362,11 @@ public class PBXProjGenerator {
|
||||
addObject(resourcesBuildPhase)
|
||||
buildPhases.append(resourcesBuildPhase.reference)
|
||||
|
||||
let headersBuildPhase = PBXHeadersBuildPhase(reference: generateUUID(PBXHeadersBuildPhase.self, target.name), files: getBuildFilesForPhase(.headers))
|
||||
addObject(headersBuildPhase)
|
||||
buildPhases.append(headersBuildPhase.reference)
|
||||
if target.type == .framework || target.type == .dynamicLibrary {
|
||||
let headersBuildPhase = PBXHeadersBuildPhase(reference: generateUUID(PBXHeadersBuildPhase.self, target.name), files: getBuildFilesForPhase(.headers))
|
||||
addObject(headersBuildPhase)
|
||||
buildPhases.append(headersBuildPhase.reference)
|
||||
}
|
||||
|
||||
if !targetFrameworkBuildFiles.isEmpty {
|
||||
|
||||
@@ -406,8 +416,8 @@ public class PBXProjGenerator {
|
||||
}
|
||||
|
||||
let carthageFrameworksToEmbed = Array(Set(carthageDependencies
|
||||
.filter { $0.embed ?? true }
|
||||
.map { $0.reference }))
|
||||
.filter { $0.embed ?? true }
|
||||
.map { $0.reference }))
|
||||
.sorted()
|
||||
|
||||
if !carthageFrameworksToEmbed.isEmpty {
|
||||
@@ -465,7 +475,7 @@ public class PBXProjGenerator {
|
||||
}
|
||||
if let fileExtension = path.extension {
|
||||
switch fileExtension {
|
||||
case "swift", "m", "cpp": return .sources
|
||||
case "swift", "m", "mm", "cpp", "c": return .sources
|
||||
case "h", "hh", "hpp", "ipp", "tpp", "hxx", "def": return .headers
|
||||
case "xcconfig", "entitlements", "gpx", "lproj", "apns": return nil
|
||||
default: return .resources
|
||||
@@ -474,94 +484,134 @@ public class PBXProjGenerator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileReference(path: Path, inPath: Path) -> String {
|
||||
func getFileReference(path: Path, inPath: Path, name: String? = nil) -> String {
|
||||
if let fileReference = fileReferencesByPath[path] {
|
||||
return fileReference
|
||||
} else {
|
||||
let fileReference = PBXFileReference(reference: generateUUID(PBXFileReference.self, path.lastComponent), sourceTree: .group, path: path.byRemovingBase(path: inPath).string)
|
||||
let fileReference = PBXFileReference(reference: generateUUID(PBXFileReference.self, path.lastComponent), sourceTree: .group, name: name, path: path.byRemovingBase(path: inPath).string)
|
||||
addObject(fileReference)
|
||||
fileReferencesByPath[path] = fileReference.reference
|
||||
return fileReference.reference
|
||||
}
|
||||
}
|
||||
|
||||
func getGroups(path: Path, depth: Int = 0) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
|
||||
func getAllSourceFiles(sources: [Source]) throws -> [SourceFile] {
|
||||
return try sources.flatMap{ try getSources(sourceMetadata: $0, path: spec.basePath + $0.path).sourceFiles }
|
||||
}
|
||||
|
||||
func getSingleGroup(path: Path, mergingChildren children: [String], depth: Int = 0) -> PBXGroup {
|
||||
let group: PBXGroup
|
||||
if let cachedGroup = groupsByPath[path] {
|
||||
cachedGroup.children += children
|
||||
group = cachedGroup
|
||||
} else {
|
||||
group = PBXGroup(
|
||||
reference: generateUUID(PBXGroup.self, path.lastComponent),
|
||||
children: children,
|
||||
sourceTree: .group,
|
||||
name: path.lastComponent,
|
||||
path: depth == 0 && !spec.options.createIntermediateGroups ?
|
||||
path.byRemovingBase(path: spec.basePath).string :
|
||||
path.lastComponent
|
||||
)
|
||||
addObject(group)
|
||||
groupsByPath[path] = group
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
// Add groups for all parents recursively
|
||||
// ex: path/foo/bar/baz/Hello.swift -> path:[foo:[bar:[baz:[Hello.swift]]]]
|
||||
func getIntermediateGroups(path: Path, group: PBXGroup) -> PBXGroup {
|
||||
// verify path is a subpath of spec.basePath
|
||||
guard Path(components: zip(path.components, spec.basePath.components).map{ $0.0 }) == spec.basePath else {
|
||||
return group
|
||||
}
|
||||
|
||||
// base case
|
||||
if path == spec.basePath {
|
||||
return group
|
||||
}
|
||||
|
||||
// recursive case
|
||||
return getIntermediateGroups(
|
||||
path: path.parent(),
|
||||
group: getSingleGroup(path: path, mergingChildren: [group.reference])
|
||||
)
|
||||
}
|
||||
|
||||
func getVariantGroup(path: Path, inPath: Path) -> PBXVariantGroup {
|
||||
let variantGroup: PBXVariantGroup
|
||||
if let cachedGroup = variantGroupsByPath[path] {
|
||||
variantGroup = cachedGroup
|
||||
} else {
|
||||
variantGroup = PBXVariantGroup(reference: generateUUID(PBXVariantGroup.self, path.byRemovingBase(path: inPath).string),
|
||||
children: [],
|
||||
name: path.lastComponent,
|
||||
sourceTree: .group)
|
||||
addObject(variantGroup)
|
||||
variantGroupsByPath[path] = variantGroup
|
||||
}
|
||||
return variantGroup
|
||||
}
|
||||
|
||||
func getSources(sourceMetadata source: Source, path: Path, depth: Int = 0) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
|
||||
// if we have a file, move it to children and use the parent as the path
|
||||
let (children, path) = path.isFile ?
|
||||
([path], path.parent()) :
|
||||
(try path.children(), path)
|
||||
|
||||
let excludedFiles: [String] = [".DS_Store"]
|
||||
|
||||
let directories = try path.children()
|
||||
let directories = children
|
||||
.filter { $0.isDirectory && $0.extension == nil && $0.extension != "lproj" }
|
||||
.sorted { $0.lastComponent < $1.lastComponent }
|
||||
|
||||
let filePaths = try path.children()
|
||||
let filePaths = children
|
||||
.filter { $0.isFile || $0.extension != nil && $0.extension != "lproj" }
|
||||
.filter { !excludedFiles.contains($0.lastComponent) }
|
||||
.sorted { $0.lastComponent < $1.lastComponent }
|
||||
|
||||
let localisedDirectories = try path.children()
|
||||
let localisedDirectories = children
|
||||
.filter { $0.extension == "lproj" }
|
||||
.sorted { $0.lastComponent < $1.lastComponent }
|
||||
|
||||
var groupChildren: [String] = filePaths.map { getFileReference(path: $0, inPath: path) }
|
||||
var allSourceFiles: [SourceFile] = filePaths.map { generateSourceFile(path: $0) }
|
||||
var allSourceFiles: [SourceFile] = filePaths.map {
|
||||
generateSourceFile(sourceMetadata: Source(path: $0.string, compilerFlags: source.compilerFlags), path: $0)
|
||||
}
|
||||
var groups: [PBXGroup] = []
|
||||
|
||||
for path in directories {
|
||||
let subGroups = try getGroups(path: path, depth: depth + 1)
|
||||
let subGroups = try getSources(sourceMetadata: source, path: path, depth: depth + 1)
|
||||
allSourceFiles += subGroups.sourceFiles
|
||||
groupChildren.append(subGroups.groups.first!.reference)
|
||||
groups += subGroups.groups
|
||||
}
|
||||
|
||||
// create variant groups of the base localisation first
|
||||
var baseLocalisationVariantGroups:[PBXVariantGroup] = []
|
||||
var baseLocalisationVariantGroups: [PBXVariantGroup] = []
|
||||
if let baseLocalisedDirectory = localisedDirectories.first(where: { $0.lastComponent == "Base.lproj" }) {
|
||||
for path in try baseLocalisedDirectory.children() {
|
||||
let filePath = "\(baseLocalisedDirectory.lastComponent)/\(path.lastComponent)"
|
||||
|
||||
let variantGroup: PBXVariantGroup
|
||||
if let cachedGroup = variantGroupsByPath[path] {
|
||||
variantGroup = cachedGroup
|
||||
} else {
|
||||
variantGroup = PBXVariantGroup(reference: generateUUID(PBXVariantGroup.self, filePath),
|
||||
children: [],
|
||||
name: path.lastComponent,
|
||||
sourceTree: .group)
|
||||
variantGroupsByPath[path] = variantGroup
|
||||
|
||||
addObject(variantGroup)
|
||||
groupChildren.append(variantGroup.reference)
|
||||
}
|
||||
|
||||
for filePath in try baseLocalisedDirectory.children() {
|
||||
let variantGroup = getVariantGroup(path: filePath, inPath: path)
|
||||
groupChildren.append(variantGroup.reference)
|
||||
baseLocalisationVariantGroups.append(variantGroup)
|
||||
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, variantGroup.reference), fileRef: variantGroup.reference, settings: nil)
|
||||
allSourceFiles.append(SourceFile(path: path, fileReference: variantGroup.reference, buildFile: buildFile))
|
||||
allSourceFiles.append(SourceFile(path: filePath, fileReference: variantGroup.reference, buildFile: buildFile))
|
||||
}
|
||||
}
|
||||
|
||||
// add references to localised resources into base localisation variant groups
|
||||
for localisedDirectory in localisedDirectories {
|
||||
let localisationName = localisedDirectory.lastComponentWithoutExtension
|
||||
for path in try localisedDirectory.children().sorted { $0.lastComponent < $1.lastComponent } {
|
||||
let filePath = "\(localisedDirectory.lastComponent)/\(path.lastComponent)"
|
||||
|
||||
for filePath in try localisedDirectory.children().sorted { $0.lastComponent < $1.lastComponent } {
|
||||
// find base localisation variant group
|
||||
let name = path.lastComponentWithoutExtension
|
||||
let variantGroup = baseLocalisationVariantGroups.first { Path($0.name!).lastComponentWithoutExtension == name }
|
||||
// ex: Foo.strings will be added to Foo.strings or Foo.storyboard variant group
|
||||
let variantGroup = baseLocalisationVariantGroups.first { Path($0.name!).lastComponent == filePath.lastComponent } ??
|
||||
baseLocalisationVariantGroups.first { Path($0.name!).lastComponentWithoutExtension == filePath.lastComponentWithoutExtension }
|
||||
|
||||
let fileReference: String
|
||||
if let cachedFileReference = fileReferencesByPath[path] {
|
||||
fileReference = cachedFileReference
|
||||
} else {
|
||||
let reference = PBXFileReference(reference: generateUUID(PBXFileReference.self, path.lastComponent),
|
||||
sourceTree: .group,
|
||||
name: variantGroup != nil ? localisationName : path.lastComponent,
|
||||
path: filePath)
|
||||
addObject(reference)
|
||||
fileReference = reference.reference
|
||||
fileReferencesByPath[path] = fileReference
|
||||
}
|
||||
let fileReference = getFileReference(path: filePath, inPath: path, name: variantGroup != nil ? localisationName : filePath.lastComponent)
|
||||
|
||||
if let variantGroup = variantGroup {
|
||||
if !variantGroup.children.contains(fileReference) {
|
||||
@@ -572,23 +622,24 @@ public class PBXProjGenerator {
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference),
|
||||
fileRef: fileReference,
|
||||
settings: nil)
|
||||
allSourceFiles.append(SourceFile(path: path, fileReference: fileReference, buildFile: buildFile))
|
||||
allSourceFiles.append(SourceFile(path: filePath, fileReference: fileReference, buildFile: buildFile))
|
||||
groupChildren.append(fileReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let groupPath: String = depth == 0 ? path.byRemovingBase(path: spec.basePath).string : path.lastComponent
|
||||
let group: PBXGroup
|
||||
if let cachedGroup = groupsByPath[path] {
|
||||
group = cachedGroup
|
||||
if spec.options.createIntermediateGroups {
|
||||
group = getIntermediateGroups(
|
||||
path: path.parent(),
|
||||
group: getSingleGroup(path: path, mergingChildren: groupChildren, depth: depth)
|
||||
)
|
||||
} else {
|
||||
group = PBXGroup(reference: generateUUID(PBXGroup.self, path.lastComponent), children: groupChildren, sourceTree: .group, name: path.lastComponent, path: groupPath)
|
||||
addObject(group)
|
||||
if depth == 0 {
|
||||
topLevelGroups.append(group)
|
||||
}
|
||||
groupsByPath[path] = group
|
||||
group = getSingleGroup(path: path, mergingChildren: groupChildren, depth: depth)
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
topLevelGroups.append(group)
|
||||
}
|
||||
groups.insert(group, at: 0)
|
||||
return (allSourceFiles, groups)
|
||||
|
||||
@@ -15,7 +15,7 @@ import ProjectSpec
|
||||
|
||||
public class ProjectGenerator {
|
||||
|
||||
var spec: ProjectSpec
|
||||
let spec: ProjectSpec
|
||||
let currentXcodeVersion = "0900"
|
||||
|
||||
public init(spec: ProjectSpec) {
|
||||
|
||||
@@ -37,7 +37,7 @@ extension ProjectSpec {
|
||||
|
||||
public func getTargetBuildSettings(target: Target, config: Config) -> BuildSettings {
|
||||
var buildSettings = BuildSettings()
|
||||
|
||||
|
||||
if options.settingPresets.applyTarget {
|
||||
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// SpecLoader.swift
|
||||
// XcodeGen
|
||||
//
|
||||
// Created by Yonas Kolb on 30/8/17.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ProjectSpec
|
||||
import PathKit
|
||||
import Yams
|
||||
import JSONUtilities
|
||||
|
||||
public struct SpecLoader {
|
||||
|
||||
public static func loadSpec(path: Path) throws -> ProjectSpec {
|
||||
let dictionary = try loadDictionary(path: path)
|
||||
return try ProjectSpec(basePath: path.parent(), jsonDictionary: dictionary)
|
||||
}
|
||||
|
||||
private static func loadDictionary(path: Path) throws -> JSONDictionary {
|
||||
var json = try loadYamlDictionary(path: path)
|
||||
|
||||
var includes: [String]
|
||||
if let includeString = json["include"] as? String {
|
||||
includes = [includeString]
|
||||
} else if let includeArray = json["include"] as? [String] {
|
||||
includes = includeArray
|
||||
} else {
|
||||
includes = []
|
||||
}
|
||||
|
||||
if !includes.isEmpty {
|
||||
var includeDictionary: JSONDictionary = [:]
|
||||
for include in includes {
|
||||
let includePath = path.parent() + include
|
||||
let dictionary = try loadDictionary(path: includePath)
|
||||
includeDictionary = merge(dictionary: dictionary, onto: includeDictionary)
|
||||
}
|
||||
json = merge(dictionary: json, onto: includeDictionary)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
private static func merge(dictionary: JSONDictionary, onto base: JSONDictionary) -> JSONDictionary {
|
||||
var merged = base
|
||||
|
||||
for (key, value) in dictionary {
|
||||
if key.hasSuffix(":REPLACE") {
|
||||
let newKey = key.replacingOccurrences(of: ":REPLACE", with: "")
|
||||
merged[newKey] = value
|
||||
} else if let dictionary = value as? JSONDictionary, let base = merged[key] as? JSONDictionary {
|
||||
merged[key] = merge(dictionary: dictionary, onto: base)
|
||||
} else if let array = value as? [Any], let base = merged[key] as? [Any] {
|
||||
merged[key] = base + array
|
||||
} else {
|
||||
merged[key] = value
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
//
|
||||
// SpecValidation.swift
|
||||
// XcodeGenKit
|
||||
//
|
||||
// Created by Yonas Kolb on 24/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ProjectSpec
|
||||
import PathKit
|
||||
|
||||
extension ProjectSpec {
|
||||
|
||||
public mutating func validate() 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 group in settings.groups {
|
||||
if let settings = settingGroups[group] {
|
||||
errors += validateSettings(settings)
|
||||
} else {
|
||||
errors.append(.invalidSettingsGroup(group))
|
||||
}
|
||||
}
|
||||
for config in settings.configSettings.keys {
|
||||
if !configs.contains(where: { $0.name.lowercased().contains(config.lowercased())}) {
|
||||
errors.append(.invalidConfigReference(config))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
for fileGroup in fileGroups {
|
||||
if !(basePath + fileGroup).exists {
|
||||
errors.append(.invalidFileGroup(fileGroup))
|
||||
}
|
||||
}
|
||||
|
||||
for (config, configFile) in configFiles {
|
||||
if !(basePath + configFile).exists {
|
||||
errors.append(.invalidConfigFile(configFile: configFile, config: config))
|
||||
}
|
||||
}
|
||||
|
||||
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 !(basePath + 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 = basePath + 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))
|
||||
}
|
||||
}
|
||||
|
||||
if scheme.configVariants.isEmpty {
|
||||
if !configs.contains(where: { $0.type == .debug }) {
|
||||
errors.append(.missingConfigTypeForGeneratedTargetScheme(target: target.name, configType: .debug))
|
||||
}
|
||||
if !configs.contains(where: { $0.type == .release }) {
|
||||
errors.append(.missingConfigTypeForGeneratedTargetScheme(target: target.name, 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 = basePath + 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 invalidConfigFile(configFile: String, config: String)
|
||||
case invalidTargetConfigFile(configFile: String, config: String, target: String)
|
||||
case invalidBuildSettingConfig(String)
|
||||
case invalidSettingsGroup(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)
|
||||
case invalidFileGroup(String)
|
||||
case invalidConfigReference(String)
|
||||
case missingConfigTypeForGeneratedTargetScheme(target: String, configType: ConfigType)
|
||||
|
||||
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 .invalidConfigFile(configFile, config): return "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 .invalidSettingsGroup(group): return "Invalid settings group \(group.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 an invalid scheme config variant 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)"
|
||||
case let .invalidFileGroup(group): return "Invalid file group \(group.quoted)"
|
||||
case let .invalidConfigReference(config): return "Invalid config reference \(config.quoted)"
|
||||
case let .missingConfigTypeForGeneratedTargetScheme(target, configType): return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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- ")
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//
|
||||
// YamsExtensions.swift
|
||||
// XcodeGenKit
|
||||
//
|
||||
// Created by Yonas Kolb on 29/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Yams
|
||||
import PathKit
|
||||
|
||||
func loadYamlDictionary(path: Path) throws -> [String: Any] {
|
||||
let string: String = try path.read()
|
||||
if string == "" {
|
||||
return [:]
|
||||
}
|
||||
guard let yaml = try Yams.load(yaml: string) else {
|
||||
return [:]
|
||||
}
|
||||
return filterNull(yaml) as! [String: Any]
|
||||
}
|
||||
|
||||
fileprivate func filterNull(_ object: Any) -> Any {
|
||||
var returnedValue: Any = object
|
||||
if let dict = object as? [String: Any] {
|
||||
var mutabledic: [String: Any] = [:]
|
||||
for (key, value) in dict {
|
||||
mutabledic[key] = filterNull(value)
|
||||
}
|
||||
returnedValue = mutabledic
|
||||
} else if let array = object as? [Any] {
|
||||
var mutableArray: [Any] = array
|
||||
for (index, value) in array.enumerated() {
|
||||
mutableArray[index] = filterNull(value)
|
||||
}
|
||||
returnedValue = mutableArray
|
||||
}
|
||||
return (object is NSNull) ? "" : returnedValue
|
||||
}
|
||||
Reference in New Issue
Block a user