mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
Merge pull request #502 from yonaskolb/refactor_spec
Refactor relative specs slightly
This commit is contained in:
@@ -33,7 +33,7 @@ public struct Project: BuildSettingsContainer {
|
||||
private var aggregateTargetsMap: [String: AggregateTarget]
|
||||
|
||||
public init(
|
||||
basePath: Path,
|
||||
basePath: Path = "",
|
||||
name: String,
|
||||
configs: [Config] = Config.defaultConfigs,
|
||||
targets: [Target] = [],
|
||||
@@ -132,16 +132,19 @@ extension Project: Equatable {
|
||||
|
||||
extension Project {
|
||||
|
||||
public init(basePath: Path, jsonDictionary: JSONDictionary) throws {
|
||||
let spec = Spec(relativePath: Path(), jsonDictionary: jsonDictionary)
|
||||
try self.init(spec: spec, basePath: basePath)
|
||||
public init(path: Path) throws {
|
||||
let spec = try SpecFile(path: path)
|
||||
try self.init(spec: spec)
|
||||
}
|
||||
|
||||
public init(spec: Spec, basePath: Path) throws {
|
||||
public init(spec: SpecFile) throws {
|
||||
try self.init(basePath: spec.basePath, jsonDictionary: spec.resolvedDictionary())
|
||||
}
|
||||
|
||||
public init(basePath: Path = "", jsonDictionary: JSONDictionary) throws {
|
||||
self.basePath = basePath
|
||||
|
||||
let spec = spec.resolvingPaths()
|
||||
let jsonDictionary = try Project.resolveProject(jsonDictionary: spec.resolvedDictionary())
|
||||
let jsonDictionary = try Project.resolveProject(jsonDictionary: jsonDictionary)
|
||||
|
||||
name = try jsonDictionary.json(atKeyPath: "name")
|
||||
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
|
||||
|
||||
@@ -2,89 +2,110 @@ import Foundation
|
||||
import JSONUtilities
|
||||
import PathKit
|
||||
|
||||
public struct Spec {
|
||||
public struct SpecFile {
|
||||
public let basePath: Path
|
||||
public let relativePath: Path
|
||||
public let jsonDictionary: JSONDictionary
|
||||
public let subSpecs: [Spec]
|
||||
public let subSpecs: [SpecFile]
|
||||
|
||||
public init(relativePath: Path, jsonDictionary: JSONDictionary, subSpecs: [Spec] = []) {
|
||||
fileprivate struct Include {
|
||||
let path: Path
|
||||
let relativePaths: Bool
|
||||
|
||||
static let defaultRelativePaths = true
|
||||
|
||||
init?(any: Any) {
|
||||
if let string = any as? String {
|
||||
path = Path(string)
|
||||
relativePaths = Include.defaultRelativePaths
|
||||
} else if let dictionary = any as? JSONDictionary,
|
||||
let path = dictionary["path"] as? String {
|
||||
self.path = Path(path)
|
||||
self.relativePaths = dictionary["relativePaths"] as? Bool ?? Include.defaultRelativePaths
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
static func parse(json: Any?) -> [Include] {
|
||||
if let array = json as? [Any] {
|
||||
return array.compactMap(Include.init)
|
||||
} else if let object = json, let include = Include(any: object) {
|
||||
return [include]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(path: Path) throws {
|
||||
try self.init(filename: path.lastComponent, basePath: path.parent())
|
||||
}
|
||||
|
||||
public init(jsonDictionary: JSONDictionary, basePath: Path = "", relativePath: Path = "", subSpecs: [SpecFile] = []) {
|
||||
self.basePath = basePath
|
||||
self.relativePath = relativePath
|
||||
self.jsonDictionary = jsonDictionary
|
||||
self.subSpecs = subSpecs
|
||||
}
|
||||
|
||||
public init(filename: String, basePath: Path, relativePath: Path = Path()) throws {
|
||||
let path = basePath + relativePath + filename
|
||||
fileprivate init(include: Include, basePath: Path, relativePath: Path) throws {
|
||||
let basePath = include.relativePaths ? (basePath + relativePath) : (basePath + relativePath + include.path.parent())
|
||||
let relativePath = include.relativePaths ? include.path.parent() : Path()
|
||||
|
||||
try self.init(filename: include.path.lastComponent, basePath: basePath, relativePath: relativePath)
|
||||
}
|
||||
|
||||
fileprivate init(filename: String, basePath: Path, relativePath: Path = "") throws {
|
||||
let path = basePath + relativePath + filename
|
||||
let jsonDictionary = try SpecFile.loadDictionary(path: path)
|
||||
|
||||
let includes = Include.parse(json: jsonDictionary["include"])
|
||||
let subSpecs: [SpecFile] = try includes.map { include in
|
||||
try SpecFile(include: include, basePath: basePath, relativePath: relativePath)
|
||||
}
|
||||
|
||||
self.init(jsonDictionary: jsonDictionary, basePath: basePath, relativePath: relativePath, subSpecs: subSpecs)
|
||||
}
|
||||
|
||||
static func loadDictionary(path: Path) throws -> JSONDictionary {
|
||||
// Depending on the extension we will either load the file as YAML or JSON
|
||||
var json: [String: Any]
|
||||
if path.extension?.lowercased() == "json" {
|
||||
let data: Data = try path.read()
|
||||
let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
|
||||
guard let jsonDictionary = jsonData as? [String: Any] else {
|
||||
fatalError("Invalid JSON at path \(path)")
|
||||
}
|
||||
json = jsonDictionary
|
||||
return jsonDictionary
|
||||
} else {
|
||||
json = try loadYamlDictionary(path: path)
|
||||
return try loadYamlDictionary(path: path)
|
||||
}
|
||||
|
||||
let processIncludeOption = { (option: Any) -> (String, Bool)? in
|
||||
if let option = option as? String {
|
||||
return (option, true)
|
||||
} else if let option = option as? JSONDictionary, let path = option["path"] as? String {
|
||||
return (path, (option["relativePaths"] as? Bool) ?? true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
let includeSources: [(String, Bool)]
|
||||
if let sources = json["include"] as? [Any] {
|
||||
includeSources = sources.compactMap { processIncludeOption($0) }
|
||||
} else if let source = json["include"] {
|
||||
includeSources = [processIncludeOption(source)].compactMap { $0 }
|
||||
} else {
|
||||
includeSources = []
|
||||
}
|
||||
|
||||
let includes = try includeSources.map { include -> Spec in
|
||||
let path = Path(include.0)
|
||||
let basePath = include.1 ? basePath + relativePath : basePath + relativePath + path.parent()
|
||||
let relativePath = include.1 ? path.parent() : Path()
|
||||
|
||||
return try Spec(filename: path.lastComponent, basePath: basePath, relativePath: relativePath)
|
||||
}
|
||||
|
||||
self.relativePath = relativePath
|
||||
self.jsonDictionary = json
|
||||
self.subSpecs = includes
|
||||
}
|
||||
|
||||
public func resolvedDictionary() -> JSONDictionary {
|
||||
let resolvedSpec = resolvingPaths()
|
||||
return resolvedSpec.mergedDictionary()
|
||||
}
|
||||
|
||||
func mergedDictionary() -> JSONDictionary {
|
||||
return jsonDictionary.merged(onto:
|
||||
subSpecs
|
||||
.map { $0.resolvedDictionary() }
|
||||
.map { $0.mergedDictionary() }
|
||||
.reduce([:]) { $1.merged(onto: $0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Spec {
|
||||
|
||||
func resolvingPaths(relativeTo basePath: Path = Path()) -> Spec {
|
||||
func resolvingPaths(relativeTo basePath: Path = Path()) -> SpecFile {
|
||||
let relativePath = (basePath + self.relativePath).normalize()
|
||||
guard relativePath != Path() else {
|
||||
return self
|
||||
}
|
||||
|
||||
let jsonDictionary = Project.pathProperties.resolvingPaths(in: self.jsonDictionary, relativeTo: relativePath)
|
||||
|
||||
return Spec(
|
||||
relativePath: self.relativePath,
|
||||
return SpecFile(
|
||||
jsonDictionary: jsonDictionary,
|
||||
subSpecs: self.subSpecs.map { template in
|
||||
return template.resolvingPaths(relativeTo: relativePath)
|
||||
}
|
||||
relativePath: self.relativePath,
|
||||
subSpecs: self.subSpecs.map { $0.resolvingPaths(relativeTo: relativePath) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import Foundation
|
||||
import JSONUtilities
|
||||
import PathKit
|
||||
|
||||
extension Project {
|
||||
|
||||
public init(path: Path) throws {
|
||||
let basePath = path.parent()
|
||||
let template = try Spec(filename: path.lastComponent, basePath: basePath)
|
||||
try self.init(spec: template, basePath: basePath)
|
||||
}
|
||||
|
||||
public static func loadDictionary(path: Path) throws -> JSONDictionary {
|
||||
return try Spec(filename: path.lastComponent, basePath: path.parent()).jsonDictionary
|
||||
}
|
||||
}
|
||||
@@ -126,8 +126,6 @@ extension SpecOptions: PathContainer {
|
||||
static var pathProperties: [PathProperty] {
|
||||
return [
|
||||
.string("carthageBuildPath"),
|
||||
.string("carthageExecutablePath"),
|
||||
.string("defaultConfig"),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@ public class SpecLoader {
|
||||
}
|
||||
|
||||
public func loadProject(path: Path) throws -> Project {
|
||||
let template = try Spec(filename: path.lastComponent, basePath: path.parent())
|
||||
let project = try Project(spec: template, basePath: path.parent())
|
||||
let spec = try SpecFile(path: path)
|
||||
let resolvedDictionary = spec.resolvedDictionary()
|
||||
let project = try Project(basePath: spec.basePath, jsonDictionary: resolvedDictionary)
|
||||
|
||||
self.project = project
|
||||
projectDictionary = template.jsonDictionary
|
||||
projectDictionary = resolvedDictionary
|
||||
|
||||
return project
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("generates bundle id") {
|
||||
let options = SpecOptions(bundleIdPrefix: "com.test")
|
||||
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
|
||||
let project = Project(name: "test", targets: [framework], options: options)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let target = pbxProj.nativeTargets.first,
|
||||
let buildConfigList = target.buildConfigurationList,
|
||||
@@ -57,7 +57,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("clears setting presets") {
|
||||
let options = SpecOptions(settingPresets: .none)
|
||||
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
|
||||
let project = Project(name: "test", targets: [framework], options: options)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let allSettings = pbxProj.buildConfigurations.reduce([:]) { $0.merged($1.buildSettings) }.keys.sorted()
|
||||
try expect(allSettings) == ["SDKROOT", "SETTING_2"]
|
||||
@@ -65,7 +65,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("generates development language") {
|
||||
let options = SpecOptions(developmentLanguage: "de")
|
||||
let project = Project(basePath: "", name: "test", options: options)
|
||||
let project = Project(name: "test", options: options)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let pbxProject = pbxProj.projects.first else {
|
||||
throw failure("Could't find PBXProject")
|
||||
@@ -93,7 +93,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("uses the default configuration name") {
|
||||
let options = SpecOptions(defaultConfig: "Bconfig")
|
||||
let project = Project(basePath: "", name: "test", configs: [Config(name: "Aconfig"), Config(name: "Bconfig")], targets: [framework], options: options)
|
||||
let project = Project(name: "test", configs: [Config(name: "Aconfig"), Config(name: "Bconfig")], targets: [framework], options: options)
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let projectConfigList = pbxProject.projects.first?.buildConfigurationList,
|
||||
@@ -111,7 +111,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
describe {
|
||||
|
||||
$0.it("generates config defaults") {
|
||||
let project = Project(basePath: "", name: "test")
|
||||
let project = Project(name: "test")
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let configs = pbxProj.buildConfigurations
|
||||
try expect(configs.count) == 2
|
||||
@@ -121,7 +121,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("generates configs") {
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
configs: [Config(name: "config1"), Config(name: "config2")]
|
||||
)
|
||||
@@ -134,7 +133,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("clears config settings when missing type") {
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
configs: [Config(name: "config")]
|
||||
)
|
||||
@@ -176,7 +174,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("applies partial config settings") {
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
configs: [
|
||||
Config(name: "Staging Debug", type: .debug),
|
||||
@@ -192,7 +189,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("sets project SDKROOT if there is only a single platform") {
|
||||
var project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [
|
||||
Target(name: "1", type: .application, platform: .iOS),
|
||||
@@ -216,7 +212,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
let otherTarget2 = Target(name: "Other2", type: .framework, platform: .iOS, dependencies: [Dependency(type: .target, reference: "Other")], transitivelyLinkDependencies: true)
|
||||
let aggregateTarget = AggregateTarget(name: "AggregateTarget", targets: ["MyApp", "MyFramework"])
|
||||
let aggregateTarget2 = AggregateTarget(name: "AggregateTarget2", targets: ["AggregateTarget"])
|
||||
let project = Project(basePath: "", name: "test", targets: [app, framework, otherTarget, otherTarget2], aggregateTargets: [aggregateTarget, aggregateTarget2])
|
||||
let project = Project(name: "test", targets: [app, framework, otherTarget, otherTarget2], aggregateTargets: [aggregateTarget, aggregateTarget2])
|
||||
|
||||
$0.it("generates aggregate targets") {
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
@@ -246,7 +242,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
func testTargets() {
|
||||
describe {
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: targets)
|
||||
let project = Project(name: "test", targets: targets)
|
||||
|
||||
$0.it("generates targets") {
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
@@ -265,7 +261,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
var testTargetWithAttributes = uiTest
|
||||
testTargetWithAttributes.settings.buildSettings["CODE_SIGN_STYLE"] = "Manual"
|
||||
let project = Project(basePath: "", name: "test", targets: [appTargetWithAttributes, framework, optionalFramework, testTargetWithAttributes])
|
||||
let project = Project(name: "test", targets: [appTargetWithAttributes, framework, optionalFramework, testTargetWithAttributes])
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let targetAttributes = pbxProject.projects.first?.targetAttributes else {
|
||||
@@ -288,7 +284,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
$0.it("generates platform version") {
|
||||
let target = Target(name: "Target", type: .application, platform: .watchOS, deploymentTarget: "2.0")
|
||||
let project = Project(basePath: "", name: "", targets: [target], options: .init(deploymentTarget: DeploymentTarget(iOS: "10.0", watchOS: "3.0")))
|
||||
let project = Project(name: "", targets: [target], options: .init(deploymentTarget: DeploymentTarget(iOS: "10.0", watchOS: "3.0")))
|
||||
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
@@ -568,7 +564,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
let targets = [app, iosFrameworkZ, staticLibrary, resourceBundle, iosFrameworkA, iosFrameworkB, appTest, appTestWithoutTransitive, stickerPack]
|
||||
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: targets,
|
||||
options: SpecOptions(transitivelyLinkDependencies: true)
|
||||
@@ -801,7 +796,6 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
dependencies: [Dependency(type: .target, reference: "target1")]
|
||||
)
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [target1, target2]
|
||||
)
|
||||
@@ -860,7 +854,7 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
]
|
||||
)
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: [app, framework, optionalFramework, uiTest])
|
||||
let project = Project(name: "test", targets: [app, framework, optionalFramework, uiTest])
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let nativeTarget = pbxProject.nativeTargets.first(where: { $0.name == app.name }) else {
|
||||
|
||||
@@ -69,7 +69,7 @@ class ProjectSpecTests: XCTestCase {
|
||||
func testValidation() {
|
||||
describe {
|
||||
|
||||
let baseProject = Project(basePath: "", name: "", configs: [Config(name: "invalid")])
|
||||
let baseProject = Project(name: "", configs: [Config(name: "invalid")])
|
||||
let invalidSettings = Settings(
|
||||
configSettings: ["invalidConfig": [:]],
|
||||
groups: ["invalidSettingGroup"]
|
||||
|
||||
@@ -45,7 +45,6 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
build: Scheme.Build(targets: [buildTarget], preActions: [preAction])
|
||||
)
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
@@ -104,7 +103,6 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
profile: Scheme.Profile(config: "Debug")
|
||||
)
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
@@ -135,7 +133,7 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
Config(name: "Production Release", type: .release),
|
||||
]
|
||||
|
||||
let project = Project(basePath: "", name: "test", configs: configs, targets: [target, framework])
|
||||
let project = Project(name: "test", configs: configs, targets: [target, framework])
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 2
|
||||
@@ -162,7 +160,7 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
var target = app
|
||||
target.scheme = TargetScheme(environmentVariables: variables)
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: [target, framework])
|
||||
let project = Project(name: "test", targets: [target, framework])
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 1
|
||||
@@ -183,7 +181,7 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
postActions: [.init(name: "Run2", script: "post", settingsTarget: "MyApp")]
|
||||
)
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: [target, framework])
|
||||
let project = Project(name: "test", targets: [target, framework])
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 1
|
||||
|
||||
@@ -12,7 +12,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
describe {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.yml"
|
||||
let project = try Project(path: path)
|
||||
let project = try loadSpec(path: path)
|
||||
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
@@ -28,7 +28,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
|
||||
$0.it("expands directories") {
|
||||
let path = fixturePath + "paths_test.yml"
|
||||
let project = try Project(path: path)
|
||||
let project = try loadSpec(path: path)
|
||||
|
||||
try expect(project.configFiles) == [
|
||||
"IncludedConfig": "paths_test/config",
|
||||
@@ -38,7 +38,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
|
||||
try expect(project.options) == SpecOptions(
|
||||
carthageBuildPath: "paths_test/recursive_test/carthage_build",
|
||||
carthageExecutablePath: "paths_test/recursive_test/carthage_executable"
|
||||
carthageExecutablePath: "carthage_executable"
|
||||
)
|
||||
|
||||
try expect(project.aggregateTargets) == [
|
||||
@@ -107,7 +107,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
|
||||
$0.it("respects directory expansion preference") {
|
||||
let path = fixturePath + "legacy_paths_test.yml"
|
||||
let project = try Project(path: path)
|
||||
let project = try loadSpec(path: path)
|
||||
|
||||
try expect(project.configFiles) == [
|
||||
"IncludedConfig": "config",
|
||||
@@ -186,7 +186,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
describe {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.json"
|
||||
let project = try Project(path: path)
|
||||
let project = try loadSpec(path: path)
|
||||
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
@@ -619,7 +619,7 @@ class SpecLoadingTests: XCTestCase {
|
||||
macOS: "10.12.1"
|
||||
)
|
||||
)
|
||||
let expected = Project(basePath: "", name: "test", options: options)
|
||||
let expected = Project(name: "test", options: options)
|
||||
let dictionary: [String: Any] = ["options": [
|
||||
"carthageBuildPath": "../Carthage/Build",
|
||||
"carthageExecutablePath": "../bin/carthage",
|
||||
@@ -657,8 +657,16 @@ fileprivate func getProjectSpec(_ project: [String: Any], file: String = #file,
|
||||
projectDictionary[key] = value
|
||||
}
|
||||
do {
|
||||
let template = Spec(relativePath: "", jsonDictionary: projectDictionary)
|
||||
return try Project(spec: template, basePath: "")
|
||||
return try Project(jsonDictionary: projectDictionary)
|
||||
} catch {
|
||||
throw failure("\(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func loadSpec(path: Path, file: String = #file, line: Int = #line) throws -> Project {
|
||||
do {
|
||||
let specLoader = SpecLoader(version: "1.1.0")
|
||||
return try specLoader.loadProject(path: path)
|
||||
} catch {
|
||||
throw failure("\(error)", file: file, line: line)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user