buildPresets

This commit is contained in:
Yonas Kolb
2017-07-26 23:18:45 +02:00
parent 4e35dc6b02
commit 9d5342d301
19 changed files with 560 additions and 382 deletions
@@ -1,50 +0,0 @@
//
// BuildSettingGroupType.swift
// XcodeGen
//
// Created by Yonas Kolb on 23/7/17.
//
//
import Foundation
import xcodeproj
import PathKit
import Yams
import JSONUtilities
enum BuildSettingsPreset {
case config(ConfigType)
case platform(Platform)
case product(PBXProductType)
case base
var path: String {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .product(product): return "Products/\(product.rawValue)"
case .base: return "base"
}
}
private static var buildSettings: [String: BuildSettings] = [:]
func getBuildSettings() throws -> BuildSettings? {
if let group = BuildSettingsPreset.buildSettings[path] {
return group
}
let settingsPath = Path(#file).parent().parent().parent() + "SettingPresets/\(path).yml"
guard settingsPath.exists,
let buildSettings = try? BuildSettings(path: settingsPath) else { return nil }
BuildSettingsPreset.buildSettings[path] = buildSettings
return buildSettings
}
}
extension BuildSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
self.init(dictionary: jsonDictionary)
}
}
+5 -24
View File
@@ -48,12 +48,8 @@ public class PBXProjGenerator {
}
public func generate() throws -> PBXProj {
let buildConfigs: [XCBuildConfiguration] = try spec.configs.map { config in
var buildSettings = config.buildSettings
if let type = config.type, let typeBuildSettings = try BuildSettingsPreset.config(type).getBuildSettings() {
buildSettings = typeBuildSettings.merged(buildSettings)
}
let buildConfigs: [XCBuildConfiguration] = spec.configs.map { config in
let buildSettings = spec.getProjectBuildSettings(config: config)
return XCBuildConfiguration(reference: id(), name: config.name, baseConfigurationReference: nil, buildSettings: buildSettings)
}
@@ -135,9 +131,10 @@ public class PBXProjGenerator {
sourceFilePaths += sourceGroups.filePaths
}
let configs: [XCBuildConfiguration] = try spec.configs.map { config in
let buildSettings = try getTargetBuildSettings(config: config.name, target: target)
let configs: [XCBuildConfiguration] = spec.configs.map { config in
let buildSettings = spec.getTargetBuildSettings(target: target, config: config)
var baseConfigurationReference: String?
if let configPath = target.configs[config.name] {
let path = basePath + configPath
baseConfigurationReference = fileReferencesByPath[path]
@@ -287,22 +284,6 @@ public class PBXProjGenerator {
return nil
}
func getTargetBuildSettings(config: String, target: Target) throws -> BuildSettings {
var buildSettings = BuildSettings()
func getBuildSettingPreset(_ type: BuildSettingsPreset) throws -> BuildSettings? {
return try type.getBuildSettings()
}
buildSettings += try getBuildSettingPreset(.base)
buildSettings += try getBuildSettingPreset(.platform(target.platform))
buildSettings += try getBuildSettingPreset(.product(target.type))
buildSettings += target.buildSettings?.buildSettings
buildSettings += target.buildSettings?.configSettings[config]
return buildSettings
}
func getFileReference(path: Path, inPath: Path) -> String {
if let fileReference = fileReferencesByPath[path] {
return fileReference
+10 -6
View File
@@ -21,28 +21,32 @@ extension Array where Element: ProjectElement {
}
}
extension BuildSettings {
extension BuildSettings: CustomStringConvertible {
init() {
public init() {
dictionary = [:]
}
static var empty = BuildSettings()
public static let empty = BuildSettings()
func merged(_ buildSettings: BuildSettings) -> BuildSettings {
public func merged(_ buildSettings: BuildSettings) -> BuildSettings {
var mergedSettings = self
mergedSettings.merge(buildSettings)
return mergedSettings
}
mutating func merge(_ buildSettings: BuildSettings) {
public mutating func merge(_ buildSettings: BuildSettings) {
for (key, value) in buildSettings.dictionary {
dictionary[key] = value
}
}
public var description: String {
return dictionary.map { "\($0) = \($1)" }.joined(separator: "\n")
}
}
func +=( lhs: inout BuildSettings, rhs: BuildSettings?) {
public func +=( lhs: inout BuildSettings, rhs: BuildSettings?) {
guard let rhs = rhs else { return }
lhs.merge(rhs)
}
+30 -6
View File
@@ -40,19 +40,39 @@ public class ProjectGenerator {
var errors: [SpecValidationError.Error] = []
func validateSettingPresets(_ settingsPresets: [String]) -> [SpecValidationError.Error] {
var errors: [SpecValidationError.Error] = []
for preset in settingsPresets {
if let settingsPreset = spec.settingPresets[preset] {
errors += validateSettingPresets(settingsPreset.settingPresets)
} else {
errors.append(.invalidSettingsPreset(preset))
}
}
return errors
}
for settingPreset in spec.settingPresets.values {
errors += validateSettingPresets(settingPreset.settingPresets)
}
for config in spec.configs {
errors += validateSettingPresets(config.settingPresets)
}
for target in spec.targets {
for dependency in target.dependencies {
if case .target(let targetName) = dependency, spec.getTarget(targetName) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: targetName))
}
}
if let buildSettings = target.buildSettings {
for config in buildSettings.configSettings.keys {
if spec.getConfig(config) == nil {
errors.append(.invalidBuildSettingConfig(config: config))
}
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 {
@@ -68,6 +88,8 @@ public class ProjectGenerator {
errors.append(.invalidTargetGeneratedSchema(target: target.name, scheme: generatedScheme, configType: .release))
}
}
errors += validateSettingPresets(target.settingPresets)
}
for scheme in spec.schemes {
@@ -185,7 +207,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidTargetDependency(target: String, dependency: String)
case invalidSchemeTarget(scheme: String, target: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidBuildSettingConfig(config: String)
case invalidBuildSettingConfig(String)
case invalidSettingsPreset(String)
case missingTargetSource(target: String, source: String)
case invalidTargetGeneratedSchema(target: String, scheme: String, configType: ConfigType)
@@ -196,6 +219,7 @@ public struct SpecValidationError: Error, CustomStringConvertible {
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 .invalidTargetGeneratedSchema(target, scheme, configType): return "Target \(target.quoted) has an invalid schema generation name which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(scheme.quoted)"
}
}
+85
View File
@@ -0,0 +1,85 @@
//
// SettingsBuilder.swift
// XcodeGen
//
// Created by Yonas Kolb on 26/7/17.
//
//
import Foundation
import xcodeproj
import PathKit
extension Spec {
public func getProjectBuildSettings(config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
buildSettings += SettingsPresetFile.base.getBuildSettings()
if let type = config.type {
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
}
for preset in config.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
buildSettings += config.settings
return buildSettings
}
public func getTargetBuildSettings(target: Target, config: Config) -> BuildSettings {
var buildSettings = BuildSettings()
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
for preset in target.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
buildSettings += getBuildSettings(settings: target.settings, config: config)
return buildSettings
}
public func getBuildSettings(preset: String, config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
let settingPreset = settingPresets[preset]!
buildSettings += getBuildSettings(settings: settingPreset.settings, config: config)
for preset in settingPreset.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
return buildSettings
}
public func getBuildSettings(settings: Settings, config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
buildSettings += settings.buildSettings
buildSettings += settings.configSettings[config.name]
return buildSettings
}
}
private var buildSettingFiles: [String: BuildSettings] = [:]
extension SettingsPresetFile {
public func getBuildSettings() -> BuildSettings? {
if let group = buildSettingFiles[path] {
return group
}
let settingsPath = Path(#file).parent().parent().parent() + "SettingPresets/\(path).yml"
guard settingsPath.exists,
let buildSettings = try? BuildSettings(path: settingsPath) else { return nil }
buildSettingFiles[path] = buildSettings
return buildSettings
}
}
@@ -1,39 +0,0 @@
//
// BuildSetting.swift
// XcodeGen
//
// Created by Yonas Kolb on 21/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct TargetBuildSettings: JSONObjectConvertible {
public let buildSettings: BuildSettings
public let configSettings: [String: BuildSettings]
public init(buildSettings: BuildSettings, configSettings: [String: BuildSettings] = [:]) {
self.buildSettings = buildSettings
self.configSettings = configSettings
}
public init(jsonDictionary: JSONDictionary) throws {
if jsonDictionary["$base"] != nil {
buildSettings = BuildSettings(dictionary: try jsonDictionary.json(atKeyPath: "$base"))
var configSettings: [String: BuildSettings] = [:]
for (key, value) in jsonDictionary {
if key != "baseSettings", let buildSettings = value as? JSONDictionary {
configSettings["key"] = BuildSettings(dictionary: buildSettings)
}
}
self.configSettings = configSettings
} else {
buildSettings = BuildSettings(dictionary: jsonDictionary)
configSettings = [:]
}
}
}
@@ -1,29 +0,0 @@
//
// BuildSettings.swift
// XcodeGen
//
// Created by Yonas Kolb on 20/7/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
public struct BuildSettingGroup {
public var name: String
public var buildSettings: BuildSettings
public init(name: String, buildSettings: BuildSettings) {
self.name = name
self.buildSettings = buildSettings
}
}
extension BuildSettingGroup: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
self.buildSettings = BuildSettings(dictionary: jsonDictionary)
}
}
+15 -8
View File
@@ -10,18 +10,25 @@ import Foundation
import xcodeproj
import JSONUtilities
public struct Config {
public struct Config: Equatable {
public var name: String
public var type: ConfigType?
public var buildSettingGroups: [BuildSettingGroup]
public var buildSettings: BuildSettings
public var settings: BuildSettings
public var settingPresets: [String]
public init(name: String, type: ConfigType? = nil, buildSettingGroups: [BuildSettingGroup] = [], buildSettings: BuildSettings = .init()) {
public init(name: String, type: ConfigType? = nil, settings: BuildSettings = .empty, settingPresets: [String] = []) {
self.name = name
self.buildSettingGroups = buildSettingGroups
self.buildSettings = buildSettings
self.settings = settings
self.settingPresets = settingPresets
self.type = type
}
public static func ==(lhs: Config, rhs: Config) -> Bool {
return lhs.name == rhs.name &&
lhs.type == rhs.type &&
lhs.settings == rhs.settings &&
lhs.settingPresets == rhs.settingPresets
}
}
@@ -35,7 +42,7 @@ extension Config: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
type = jsonDictionary.json(atKeyPath: "type")
buildSettingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
buildSettings = BuildSettings(dictionary: jsonDictionary.json(atKeyPath: "buildSettings") ?? [:])
settings = BuildSettings(dictionary: jsonDictionary.json(atKeyPath: "settings") ?? [:])
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? []
}
}
@@ -0,0 +1,58 @@
//
// SettingPreset.swift
// XcodeGen
//
// Created by Yonas Kolb on 26/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct SettingPreset: Equatable, CustomStringConvertible {
public var settings: Settings
public var settingPresets: [String]
public init(settings: Settings, settingPresets: [String] = []) {
self.settings = settings
self.settingPresets = settingPresets
}
public static func ==(lhs: SettingPreset, rhs: SettingPreset) -> Bool {
return lhs.settings == rhs.settings && lhs.settingPresets == rhs.settingPresets
}
public var description: String {
return "\(settings)\nPresets: \(settingPresets)"
}
}
public enum SettingsPresetFile {
case config(ConfigType)
case platform(Platform)
case product(PBXProductType)
case base
var path: String {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .product(product): return "Products/\(product.rawValue)"
case .base: return "base"
}
}
}
extension SettingPreset: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
if jsonDictionary["settings"] == nil {
settings = try Settings(jsonDictionary: jsonDictionary)
settingPresets = []
} else {
settings = try jsonDictionary.json(atKeyPath: "settings")
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? []
}
}
}
+73
View File
@@ -0,0 +1,73 @@
//
// BuildSetting.swift
// XcodeGen
//
// Created by Yonas Kolb on 21/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct Settings: Equatable, JSONObjectConvertible, CustomStringConvertible {
public let buildSettings: BuildSettings
public let configSettings: [String: BuildSettings]
public init(buildSettings: BuildSettings, configSettings: [String: BuildSettings] = [:]) {
self.buildSettings = buildSettings
self.configSettings = configSettings
}
public init(dictionary: [String: Any]) {
self.buildSettings = BuildSettings(dictionary: dictionary)
self.configSettings = [:]
}
static let empty: Settings = Settings(buildSettings: BuildSettings())
public init(jsonDictionary: JSONDictionary) throws {
if let configSettings: [String: BuildSettings] = jsonDictionary.json(atKeyPath: "configs") {
buildSettings = jsonDictionary.json(atKeyPath: "default") ?? [:]
self.configSettings = configSettings
} else {
buildSettings = BuildSettings(dictionary: jsonDictionary)
configSettings = [:]
}
}
public static func ==(lhs: Settings, rhs: Settings) -> Bool {
return lhs.buildSettings == rhs.buildSettings && lhs.configSettings == rhs.configSettings
}
public var description: String {
var string: String = ""
if !buildSettings.dictionary.isEmpty {
string += buildSettings.description
}
for (config, buildSettings) in configSettings {
if !buildSettings.dictionary.isEmpty {
string += "\n\(config)\n\t" + buildSettings.description.replacingOccurrences(of: "\n", with: "\n\t")
}
}
return string
}
}
extension Settings: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, Any)...) {
var dictionary: [String: Any] = [:]
elements.forEach { dictionary[$0.0] = $0.1 }
self.init(dictionary: dictionary)
}
}
extension BuildSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
self.init(dictionary: jsonDictionary)
}
}
+9 -6
View File
@@ -16,23 +16,25 @@ public struct Spec {
public var name: String
public var targets: [Target]
public var settingGroups: [BuildSettingGroup]
public var settings: Settings
public var settingPresets: [String: SettingPreset]
public var configs: [Config]
public var schemes: [Scheme]
public init(name: String, configs: [Config] = [], targets: [Target] = [], settingGroups: [BuildSettingGroup] = [], schemes: [Scheme] = []) {
public init(name: String, configs: [Config] = [], targets: [Target] = [], settings: Settings = .empty, settingPresets: [String: SettingPreset] = [:], schemes: [Scheme] = []) {
self.name = name
self.targets = targets
self.configs = configs
self.settingGroups = settingGroups
self.settings = settings
self.settingPresets = settingPresets
self.schemes = schemes
}
func getTarget(_ targetName: String) -> Target? {
public func getTarget(_ targetName: String) -> Target? {
return targets.first { $0.name == targetName }
}
func getConfig(_ configName: String) -> Config? {
public func getConfig(_ configName: String) -> Config? {
return configs.first { $0.name == configName }
}
}
@@ -53,7 +55,8 @@ extension Spec {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
settingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? [:]
configs = try jsonDictionary.json(atKeyPath: "configs")
if jsonDictionary["targets"] == nil {
targets = []
+7 -4
View File
@@ -14,7 +14,8 @@ public struct Target {
public var name: String
public var type: PBXProductType
public var platform: Platform
public var buildSettings: TargetBuildSettings?
public var settings: Settings
public var settingPresets: [String]
public var sources: [String]
public var sourceExludes: [String]
public var dependencies: [Dependency]
@@ -31,11 +32,12 @@ public struct Target {
return name
}
public init(name: String, type: PBXProductType, platform: Platform, buildSettings: TargetBuildSettings?, configs: [String: String] = [:], sources: [String] = [], sourceExludes: [String] = [], dependencies: [Dependency] = [], prebuildScripts: [String] = [], postbuildScripts: [String] = [], generateSchemes: [String] = []) {
public init(name: String, type: PBXProductType, platform: Platform, settings: Settings = .empty, settingPresets: [String] = [], configs: [String: String] = [:], sources: [String] = [], sourceExludes: [String] = [], dependencies: [Dependency] = [], prebuildScripts: [String] = [], postbuildScripts: [String] = [], generateSchemes: [String] = []) {
self.name = name
self.type = type
self.platform = platform
self.buildSettings = buildSettings
self.settings = settings
self.settingPresets = settingPresets
self.configs = configs
self.sources = sources
self.sourceExludes = sourceExludes
@@ -62,7 +64,8 @@ extension Target: JSONObjectConvertible {
} else {
throw SpecError.unknownTargetPlatform(platformString)
}
buildSettings = jsonDictionary.json(atKeyPath: "buildSettings")
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
settingPresets = jsonDictionary.json(atKeyPath: "settingsPresets") ?? []
configs = jsonDictionary.json(atKeyPath: "configs") ?? [:]
if let source: String = jsonDictionary.json(atKeyPath: "sources") {
sources = [source]