mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-04-23 11:26:54 +02:00
4647310322
Based on Swiftgram 12.5 (Telegram iOS 12.5). All GLEGram features ported and organized in GLEGram/ folder. Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass, Font Replacement, Fake Profile, Chat Export, Plugin System, and more. See CHANGELOG_12.5.md for full details.
320 lines
12 KiB
Swift
320 lines
12 KiB
Swift
import Foundation
|
|
import JSONUtilities
|
|
import PathKit
|
|
import ProjectSpec
|
|
import XcodeProj
|
|
import Yams
|
|
|
|
extension Project {
|
|
|
|
public func getProjectBuildSettings(config: Config) -> BuildSettings {
|
|
var buildSettings: BuildSettings = [:]
|
|
|
|
// set project SDKROOT is a single platform
|
|
if let firstPlatform = targets.first?.platform,
|
|
targets.allSatisfy({ $0.platform == firstPlatform })
|
|
{
|
|
buildSettings["SDKROOT"] = firstPlatform.sdkRoot
|
|
}
|
|
|
|
if let type = config.type, options.settingPresets.applyProject {
|
|
buildSettings += SettingsPresetFile.base.getBuildSettings()
|
|
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
|
|
}
|
|
|
|
// apply custom platform version
|
|
for platform in Platform.allCases {
|
|
if let version = options.deploymentTarget.version(for: platform) {
|
|
buildSettings[platform.deploymentTargetSetting] = version.deploymentTarget
|
|
}
|
|
}
|
|
|
|
// Prevent setting presets from overwriting 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()
|
|
|
|
// list of supported destination sorted by priority
|
|
let specSupportedDestinations = target.supportedDestinations?.sorted(by: { $0.priority < $1.priority }) ?? []
|
|
|
|
if options.settingPresets.applyTarget {
|
|
let platform: Platform
|
|
|
|
if target.platform == .auto,
|
|
let firstDestination = specSupportedDestinations.first,
|
|
let firstDestinationPlatform = Platform(rawValue: firstDestination.rawValue) {
|
|
|
|
platform = firstDestinationPlatform
|
|
} else {
|
|
platform = target.platform
|
|
}
|
|
|
|
buildSettings += SettingsPresetFile.platform(platform).getBuildSettings()
|
|
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
|
|
buildSettings += SettingsPresetFile.productPlatform(target.type, platform).getBuildSettings()
|
|
|
|
if target.platform == .auto {
|
|
// this fix is necessary because the platform preset overrides the original value
|
|
buildSettings["SDKROOT"] = Platform.auto.rawValue
|
|
}
|
|
}
|
|
|
|
if !specSupportedDestinations.isEmpty {
|
|
var supportedPlatforms: [String] = []
|
|
var targetedDeviceFamily: [String] = []
|
|
|
|
for supportedDestination in specSupportedDestinations {
|
|
let supportedPlatformBuildSettings = SettingsPresetFile.supportedDestination(supportedDestination).getBuildSettings()
|
|
buildSettings += supportedPlatformBuildSettings
|
|
|
|
if let value = supportedPlatformBuildSettings?["SUPPORTED_PLATFORMS"] as? String {
|
|
supportedPlatforms += value.components(separatedBy: " ")
|
|
}
|
|
if let value = supportedPlatformBuildSettings?["TARGETED_DEVICE_FAMILY"] as? String {
|
|
targetedDeviceFamily += value.components(separatedBy: ",")
|
|
}
|
|
}
|
|
|
|
buildSettings["SUPPORTED_PLATFORMS"] = supportedPlatforms.joined(separator: " ")
|
|
buildSettings["TARGETED_DEVICE_FAMILY"] = targetedDeviceFamily.joined(separator: ",")
|
|
}
|
|
|
|
// apply custom platform version
|
|
if let version = target.deploymentTarget {
|
|
if !specSupportedDestinations.isEmpty {
|
|
for supportedDestination in specSupportedDestinations {
|
|
if let platform = Platform(rawValue: supportedDestination.rawValue) {
|
|
buildSettings[platform.deploymentTargetSetting] = version.deploymentTarget
|
|
}
|
|
}
|
|
} else {
|
|
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
let isPartialMatch = config.name.lowercased().contains(configVariant.lowercased())
|
|
if isPartialMatch {
|
|
let exactConfig = getConfig(configVariant)
|
|
let matchesExactlyToOtherConfig = exactConfig != nil && exactConfig?.name != config.name
|
|
if !matchesExactlyToOtherConfig {
|
|
buildSettings += getBuildSettings(settings: settings, config: config)
|
|
}
|
|
}
|
|
}
|
|
|
|
return buildSettings
|
|
}
|
|
|
|
// combines all levels of a target's settings: target, target config, project, project config
|
|
public func getCombinedBuildSetting(_ setting: String, target: ProjectTarget, config: Config) -> Any? {
|
|
if let target = target as? Target,
|
|
let value = getTargetBuildSettings(target: target, config: config)[setting] {
|
|
return value
|
|
}
|
|
if let configFilePath = target.configFiles[config.name],
|
|
let value = loadConfigFileBuildSettings(path: configFilePath)?[setting] {
|
|
return value
|
|
}
|
|
if let value = getProjectBuildSettings(config: config)[setting] {
|
|
return value
|
|
}
|
|
if let configFilePath = configFiles[config.name],
|
|
let value = loadConfigFileBuildSettings(path: configFilePath)?[setting] {
|
|
return value
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public func getBoolBuildSetting(_ setting: String, target: ProjectTarget, config: Config) -> Bool? {
|
|
guard let value = getCombinedBuildSetting(setting, target: target, config: config) else { return nil }
|
|
|
|
if let boolValue = value as? Bool {
|
|
return boolValue
|
|
} else if let stringValue = value as? String {
|
|
return stringValue == "YES"
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
public func targetHasBuildSetting(_ setting: String, target: Target, config: Config) -> Bool {
|
|
getCombinedBuildSetting(setting, target: target, config: config) != 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 cached = configFileSettings[configFilePath.string] {
|
|
return cached.value
|
|
} else {
|
|
guard let configFile = try? XCConfig(path: configFilePath) else {
|
|
configFileSettings[configFilePath.string] = .nothing
|
|
return nil
|
|
}
|
|
let settings = configFile.flattenedBuildSettings()
|
|
configFileSettings[configFilePath.string] = .cached(settings)
|
|
return settings
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum Cached<T> {
|
|
case cached(T)
|
|
case nothing
|
|
|
|
var value: T? {
|
|
switch self {
|
|
case let .cached(value): return value
|
|
case .nothing: return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// cached flattened xcconfig file settings
|
|
private var configFileSettings: [String: Cached<BuildSettings>] = [:]
|
|
|
|
// cached setting preset settings
|
|
private var settingPresetSettings: [String: Cached<BuildSettings>] = [:]
|
|
|
|
extension SettingsPresetFile {
|
|
|
|
public func getBuildSettings() -> BuildSettings? {
|
|
if let cached = settingPresetSettings[path] {
|
|
return cached.value
|
|
}
|
|
let bundlePath = Path(Bundle.main.bundlePath)
|
|
let relativePath = Path("SettingPresets/\(path).yml")
|
|
var possibleSettingsPaths: [Path] = [
|
|
relativePath,
|
|
bundlePath + relativePath,
|
|
bundlePath + "../share/xcodegen/\(relativePath)",
|
|
Path(#file).parent().parent().parent() + relativePath,
|
|
]
|
|
|
|
if let resourcePath = Bundle.main.resourcePath {
|
|
possibleSettingsPaths.append(Path(resourcePath) + relativePath)
|
|
}
|
|
|
|
if let symlink = try? (bundlePath + "xcodegen").symlinkDestination() {
|
|
possibleSettingsPaths = [
|
|
symlink.parent() + relativePath,
|
|
] + possibleSettingsPaths
|
|
}
|
|
if let moduleResourcePath = Bundle.availableModule?.path(forResource: "SettingPresets", ofType: nil) {
|
|
possibleSettingsPaths.append(Path(moduleResourcePath) + "\(path).yml")
|
|
}
|
|
|
|
guard let settingsPath = possibleSettingsPaths.first(where: { $0.exists }) else {
|
|
switch self {
|
|
case .base, .config, .platform, .supportedDestination:
|
|
print("No \"\(name)\" settings found")
|
|
case .product, .productPlatform:
|
|
break
|
|
}
|
|
settingPresetSettings[path] = .nothing
|
|
return nil
|
|
}
|
|
|
|
guard let buildSettings = try? loadYamlDictionary(path: settingsPath) else {
|
|
print("Error parsing \"\(name)\" settings")
|
|
return nil
|
|
}
|
|
settingPresetSettings[path] = .cached(buildSettings)
|
|
return buildSettings
|
|
}
|
|
}
|
|
|
|
private class BundleFinder {}
|
|
|
|
/// The default SPM generated `Bundle.module` crashes on runtime if there is no .bundle file.
|
|
/// Below implementation modified from generated `Bundle.module` code which call `fatalError` if .bundle file not found.
|
|
private extension Bundle {
|
|
/// Returns the resource bundle associated with the current Swift module.
|
|
static let availableModule: Bundle? = {
|
|
let bundleName = "XcodeGen_XcodeGenKit"
|
|
|
|
let overrides: [URL]
|
|
#if DEBUG
|
|
// The 'PACKAGE_RESOURCE_BUNDLE_PATH' name is preferred since the expected value is a path. The
|
|
// check for 'PACKAGE_RESOURCE_BUNDLE_URL' will be removed when all clients have switched over.
|
|
// This removal is tracked by rdar://107766372.
|
|
if let override = ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_PATH"]
|
|
?? ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_URL"] {
|
|
overrides = [URL(fileURLWithPath: override)]
|
|
} else {
|
|
overrides = []
|
|
}
|
|
#else
|
|
overrides = []
|
|
#endif
|
|
|
|
let candidates = overrides + [
|
|
// Bundle should be present here when the package is linked into an App.
|
|
Bundle.main.resourceURL,
|
|
|
|
// Bundle should be present here when the package is linked into a framework.
|
|
Bundle(for: BundleFinder.self).resourceURL,
|
|
|
|
// For command-line tools.
|
|
Bundle.main.bundleURL,
|
|
]
|
|
|
|
for candidate in candidates {
|
|
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
|
|
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
|
|
return bundle
|
|
}
|
|
}
|
|
return nil
|
|
}()
|
|
}
|