XcodeGen/Sources/XcodeGenKit/SettingsBuilder.swift
2017-11-22 13:01:17 +01:00

159 lines
6.2 KiB
Swift

import Foundation
import xcproj
import PathKit
import ProjectSpec
import Yams
import JSONUtilities
extension ProjectSpec {
public func getProjectBuildSettings(config: Config) -> BuildSettings {
var buildSettings: BuildSettings = [:]
if let type = config.type, options.settingPresets.applyProject {
buildSettings += SettingsPresetFile.base.getBuildSettings()
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
}
// Prevent setting presets from overrwriting settings in project xcconfig files
if let configPath = configFiles[config.name] {
buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath)
}
buildSettings += getBuildSettings(settings: settings, config: config)
return buildSettings
}
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()
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
}
// Prevent setting presets from overrwriting settings in target xcconfig files
if let configPath = target.configFiles[config.name] {
buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath)
}
// Prevent setting presets from overrwriting settings in project xcconfig files
if let configPath = configFiles[config.name] {
buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath)
}
buildSettings += getBuildSettings(settings: target.settings, config: config)
return buildSettings
}
public func getBuildSettings(settings: Settings, config: Config) -> BuildSettings {
var buildSettings: BuildSettings = [:]
for group in settings.groups {
if let settings = settingGroups[group] {
buildSettings += getBuildSettings(settings: settings, config: config)
}
}
buildSettings += settings.buildSettings
for (configVariant, settings) in settings.configSettings {
if config.name.lowercased().contains(configVariant.lowercased()) {
buildSettings += getBuildSettings(settings: settings, config: config)
}
}
return buildSettings
}
// combines all levels of a target's settings: target, target config, project, project config
public func getCombinedBuildSettings(basePath: Path, target: Target, config: Config, includeProject: Bool = true) -> BuildSettings {
var buildSettings: BuildSettings = [:]
if includeProject {
if let configFilePath = configFiles[config.name] {
buildSettings += loadConfigFileBuildSettings(path: configFilePath)
}
buildSettings += getProjectBuildSettings(config: config)
}
if let configFilePath = target.configFiles[config.name] {
buildSettings += loadConfigFileBuildSettings(path: configFilePath)
}
buildSettings += getTargetBuildSettings(target: target, config: config)
return buildSettings
}
public func targetHasBuildSetting(_ setting: String, basePath: Path, target: Target, config: Config, includeProject: Bool = true) -> Bool {
let buildSettings = getCombinedBuildSettings(basePath: basePath, target: target, config: config, includeProject: includeProject)
return buildSettings[setting] != nil
}
/// Removes values from build settings if they are defined in an xcconfig file
private func removeConfigFileSettings(from buildSettings: BuildSettings, configPath: String) -> BuildSettings {
var buildSettings = buildSettings
if let configSettings = loadConfigFileBuildSettings(path: configPath) {
for key in configSettings.keys {
// FIXME: Catch platform specifier. e.g. LD_RUNPATH_SEARCH_PATHS[sdk=iphone*]
buildSettings.removeValue(forKey: key)
buildSettings.removeValue(forKey: key.quoted)
}
}
return buildSettings
}
/// Returns cached build settings from a config file
private func loadConfigFileBuildSettings(path: String) -> BuildSettings? {
let configFilePath = basePath + path
if let settings = configFileSettings[configFilePath.string] {
return settings
} else {
guard let configFile = try? XCConfig(path: configFilePath) else { return nil }
let settings = configFile.flattenedBuildSettings()
configFileSettings[configFilePath.string] = settings
return settings
}
}
}
// cached flattened xcconfig file settings
private var configFileSettings: [String: BuildSettings] = [:]
// cached setting preset settings
private var settingPresetSettings: [String: BuildSettings] = [:]
extension SettingsPresetFile {
public func getBuildSettings() -> BuildSettings? {
if let group = settingPresetSettings[path] {
return group
}
let relativePath = "SettingPresets/\(path).yml"
let possibleSettingsPaths: [Path] = [
Path(relativePath),
Path(Bundle.main.bundlePath) + relativePath,
Path(Bundle.main.bundlePath) + "../share/xcodegen/\(relativePath)",
Path(#file).parent().parent().parent() + relativePath,
]
guard let settingsPath = possibleSettingsPaths.first(where: { $0.exists }) else {
switch self {
case .base, .config, .platform:
print("No \"\(name)\" settings found")
case .product, .productPlatform:
break
}
return nil
}
guard let buildSettings = try? loadYamlDictionary(path: settingsPath) else {
print("Error parsing \"\(name)\" settings")
return nil
}
settingPresetSettings[path] = buildSettings
return buildSettings
}
}