From e927d8f4b1d2704b53c21b24b8837e9314cc99c4 Mon Sep 17 00:00:00 2001 From: Brian Kelley Date: Thu, 1 Mar 2018 17:28:50 -0800 Subject: [PATCH] Load Xcode Project-Relative XCConfig Files If an xcconfig file includes another xcconfig file with a relative path, Xcode will first attempt to load the included file relative to the including xcconfig's path. If that fails, Xcode will attempt to load the included xcconfig file relative to the Xcode project. Add support for the latter form of include resolution. --- Sources/xcproj/XCConfig.swift | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Sources/xcproj/XCConfig.swift b/Sources/xcproj/XCConfig.swift index fd7bd38d1..5999c4608 100644 --- a/Sources/xcproj/XCConfig.swift +++ b/Sources/xcproj/XCConfig.swift @@ -29,12 +29,13 @@ final public class XCConfig { /// Initializes the XCConfig reading the content from the file at the given path and parsing it. /// /// - Parameter path: path where the .xcconfig file is. + /// - Parameter proejctPath: path where the .xcodeproj is, for resolving project-relative includes. /// - Throws: an error if the config file cannot be found or it has an invalid format. - public init(path: Path) throws { + public init(path: Path, projectPath: Path? = nil) throws { if !path.exists { throw XCConfigError.notFound(path: path) } let fileLines = try path.read().components(separatedBy: "\n") self.includes = fileLines - .flatMap(XCConfigParser.configFrom(path: path)) + .flatMap(XCConfigParser.configFrom(path: path, projectPath: projectPath)) var buildSettings: [String: String] = [:] fileLines .flatMap(XCConfigParser.settingFrom) @@ -49,8 +50,9 @@ final class XCConfigParser { /// and returns the include path and the config that the include is pointing to. /// /// - Parameter path: path of the config file that the line belongs to. + /// - Parameter proejctPath: path where the .xcodeproj is, for resolving project-relative includes. /// - Returns: function that parses the line. - static func configFrom(path: Path) -> (String) -> (include: Path, config: XCConfig)? { + static func configFrom(path: Path, projectPath: Path?) -> (String) -> (include: Path, config: XCConfig)? { return { line in return includeRegex.matches(in: line, options: NSRegularExpression.MatchingOptions(rawValue: 0), @@ -66,9 +68,17 @@ final class XCConfigParser { let includePath: Path = Path(pathString) var config: XCConfig? if includePath.isRelative { - config = try? XCConfig(path: path.parent() + includePath) + do { + // first try to load the included xcconfig relative to the current xcconfig + config = try XCConfig(path: path.parent() + includePath, projectPath: projectPath) + } catch (XCConfigError.notFound(_)) where projectPath != nil { + // if that fails, try to load the included xcconfig relative to the project + config = try? XCConfig(path: projectPath!.parent() + includePath, projectPath: projectPath) + } catch { + config = nil + } } else { - config = try? XCConfig(path: includePath) + config = try? XCConfig(path: includePath, projectPath: projectPath) } return config.map { (includePath, $0) } }