diff --git a/Generator/Sources/NeedleFramework/Needle.swift b/Generator/Sources/NeedleFramework/Needle.swift index 0217c1eb..44b22d83 100644 --- a/Generator/Sources/NeedleFramework/Needle.swift +++ b/Generator/Sources/NeedleFramework/Needle.swift @@ -41,7 +41,7 @@ public class Needle { /// - parameter destinationPath: The path to export generated code to. public static func generate(from sourceRootPaths: [String], excludingFilesEndingWith exclusionSuffixes: [String], excludingFilesWithPaths exclusionPaths: [String], with additionalImports: [String], _ headerDocPath: String?, to destinationPath: String) { let sourceRootUrls = sourceRootPaths.map { (path: String) -> URL in - URL(fileURLWithPath: path) + URL(path: path) } #if DEBUG let executor: SequenceExecutor = ProcessInfo().environment["SINGLE_THREADED"] != nil ? SerialSequenceExecutor() : ConcurrentSequenceExecutor(name: "Needle.generate", qos: .userInteractive) diff --git a/Generator/Sources/NeedleFramework/Parsing/DependencyGraphParser.swift b/Generator/Sources/NeedleFramework/Parsing/DependencyGraphParser.swift index 2057ba06..41c167f3 100644 --- a/Generator/Sources/NeedleFramework/Parsing/DependencyGraphParser.swift +++ b/Generator/Sources/NeedleFramework/Parsing/DependencyGraphParser.swift @@ -68,8 +68,8 @@ class DependencyGraphParser { } } catch { switch error { - case FileEnumerationError.failedToReadSourcesList(let sourceListUrl): - fatalError("Failed to read source paths from list file at \(sourceListUrl)") + case FileEnumerationError.failedToReadSourcesList(let sourceListUrl, let error): + fatalError("Failed to read source paths from list file at \(sourceListUrl) \(error)") case FileEnumerationError.failedToTraverseDirectory(let dirUrl): fatalError("Failed traverse \(dirUrl)") default: diff --git a/Generator/Sources/NeedleFramework/Parsing/Pluginized/PluginizedDependencyGraphParser.swift b/Generator/Sources/NeedleFramework/Parsing/Pluginized/PluginizedDependencyGraphParser.swift index adf043ce..90fba382 100644 --- a/Generator/Sources/NeedleFramework/Parsing/Pluginized/PluginizedDependencyGraphParser.swift +++ b/Generator/Sources/NeedleFramework/Parsing/Pluginized/PluginizedDependencyGraphParser.swift @@ -61,8 +61,8 @@ class PluginizedDependencyGraphParser { } } catch { switch error { - case FileEnumerationError.failedToReadSourcesList(let sourceListUrl): - fatalError("Failed to read source paths from list file at \(sourceListUrl)") + case FileEnumerationError.failedToReadSourcesList(let sourceListUrl, let error): + fatalError("Failed to read source paths from list file at \(sourceListUrl) \(error)") case FileEnumerationError.failedToTraverseDirectory(let dirUrl): fatalError("Failed traverse \(dirUrl)") default: diff --git a/Generator/Sources/NeedleFramework/Pluginized/PluginizedNeedle.swift b/Generator/Sources/NeedleFramework/Pluginized/PluginizedNeedle.swift index 0e843b70..a9e33703 100644 --- a/Generator/Sources/NeedleFramework/Pluginized/PluginizedNeedle.swift +++ b/Generator/Sources/NeedleFramework/Pluginized/PluginizedNeedle.swift @@ -38,7 +38,7 @@ public class PluginizedNeedle { /// - parameter destinationPath: The path to export generated code to. public static func generate(from sourceRootPaths: [String], excludingFilesEndingWith exclusionSuffixes: [String], excludingFilesWithPaths exclusionPaths: [String], with additionalImports: [String], _ headerDocPath: String?, to destinationPath: String) { let sourceRootUrls = sourceRootPaths.map { (path: String) -> URL in - URL(fileURLWithPath: path) + URL(path: path) } #if DEBUG let executor: SequenceExecutor = ProcessInfo().environment["SINGLE_THREADED"] != nil ? SerialSequenceExecutor() : ConcurrentSequenceExecutor(name: "PluginizedNeedle.generate", qos: .userInteractive) diff --git a/Generator/Sources/NeedleFramework/Utilities/Extensions.swift b/Generator/Sources/NeedleFramework/Utilities/Extensions.swift index 36a3838c..56a0cb6c 100644 --- a/Generator/Sources/NeedleFramework/Utilities/Extensions.swift +++ b/Generator/Sources/NeedleFramework/Utilities/Extensions.swift @@ -43,4 +43,33 @@ extension String { } return String(self[range]) } + + /// Check if this path represents a directory. + /// + /// - note: Use this property instead of `URL.isFileURL` property, since + /// that property only checks for URL scheme, which can be inaccurate. + var isDirectory: Bool { + var isDirectory = ObjCBool(false) + FileManager.default.fileExists(atPath: self, isDirectory: &isDirectory) + return isDirectory.boolValue + } +} + +/// Utility URL extensions. +extension URL { + + /// Initializer. + /// + /// - note: This initializer first checks if the given path is a directory. + /// If so, it initializes a directory URL. Otherwise a URL with the `file` + /// scheme is initialized. This allows the returned URL to correctly return + /// the `isFileURL` property. + /// - parameter path: The `String` path to use. + init(path: String) { + if path.isDirectory { + self.init(string: path)! + } else { + self.init(fileURLWithPath: path) + } + } } diff --git a/Generator/Sources/NeedleFramework/Utilities/FileEnumerator.swift b/Generator/Sources/NeedleFramework/Utilities/FileEnumerator.swift index 27218e98..8d5f9868 100644 --- a/Generator/Sources/NeedleFramework/Utilities/FileEnumerator.swift +++ b/Generator/Sources/NeedleFramework/Utilities/FileEnumerator.swift @@ -20,7 +20,7 @@ import Foundation enum FileEnumerationError: Error { /// Failed to read the text file that is supposed to contain a list /// of paths on each line. - case failedToReadSourcesList(URL) + case failedToReadSourcesList(URL, Error) /// Failed to traverse a directory specified by given URL. case failedToTraverseDirectory(URL) } @@ -68,7 +68,7 @@ class FileEnumerator { } return paths } catch { - throw FileEnumerationError.failedToReadSourcesList(listUrl) + throw FileEnumerationError.failedToReadSourcesList(listUrl, error) } } diff --git a/Generator/Tests/NeedleFrameworkTests/Parsing/ExtensionsTests.swift b/Generator/Tests/NeedleFrameworkTests/Parsing/ExtensionsTests.swift new file mode 100644 index 00000000..53cedd40 --- /dev/null +++ b/Generator/Tests/NeedleFrameworkTests/Parsing/ExtensionsTests.swift @@ -0,0 +1,51 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +import XCTest +@testable import NeedleFramework + +class ExtensionsTests: AbstractParserTests { + + static var allTests = [ + ("test_urlInit_verifyIsFileURL", test_urlInit_verifyIsFileURL), + ] + + private var filePath: String! + private var dirPath: String! + + override func setUp() { + super.setUp() + + filePath = "\(#file)" + let index = filePath.lastIndex(of: "/") + dirPath = String(filePath.prefix(upTo: index!)) + } + + func test_isDirectory_verifyResults() { + XCTAssertFalse(filePath.isDirectory) + XCTAssertTrue(dirPath.isDirectory) + } + + func test_urlInit_verifyIsFileURL() { + let fileUrl = URL(path: filePath) + XCTAssertTrue(fileUrl.isFileURL) + + let dirUrl = URL(path: dirPath) + XCTAssertFalse(dirUrl.isFileURL) + } +} diff --git a/Generator/Tests/NeedleFrameworkTests/Parsing/FileEnumeratorTests.swift b/Generator/Tests/NeedleFrameworkTests/Parsing/FileEnumeratorTests.swift index 851cbad1..c01e561a 100644 --- a/Generator/Tests/NeedleFrameworkTests/Parsing/FileEnumeratorTests.swift +++ b/Generator/Tests/NeedleFrameworkTests/Parsing/FileEnumeratorTests.swift @@ -96,7 +96,7 @@ class FileEnumeratorTests: AbstractParserTests { XCTFail() } catch { switch error { - case FileEnumerationError.failedToReadSourcesList(let url): + case FileEnumerationError.failedToReadSourcesList(let url, _): XCTAssertEqual(url, sourcesListUrl) default: XCTFail()