Skip to content

Commit f649350

Browse files
committed
Merge branch 'move-walk-to-filesystem' of https://github.com/aciidb0mb3r/swift-package-manager
2 parents 8a1a5d0 + 98ed674 commit f649350

File tree

7 files changed

+65
-73
lines changed

7 files changed

+65
-73
lines changed

Sources/Basic/FileSystem.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ public protocol FileSystem {
9696
/// Check whether the given path is accessible and a file.
9797
func isFile(_ path: AbsolutePath) -> Bool
9898

99+
/// Check whether the given path is accessible and is a symbolic link.
100+
func isSymlink(_ path: AbsolutePath) -> Bool
101+
99102
/// Get the contents of the given directory, in an undefined order.
100103
//
101104
// FIXME: Actual file system interfaces will allow more efficient access to
@@ -145,6 +148,10 @@ private class LocalFileSystem: FileSystem {
145148
func isFile(_ path: AbsolutePath) -> Bool {
146149
return Basic.isFile(path)
147150
}
151+
152+
func isSymlink(_ path: AbsolutePath) -> Bool {
153+
return Basic.isSymlink(path)
154+
}
148155

149156
func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
150157
guard let dir = libc.opendir(path.asString) else {
@@ -353,6 +360,12 @@ public class InMemoryFileSystem: FileSystem {
353360
return false
354361
}
355362
}
363+
364+
public func isSymlink(_ path: AbsolutePath) -> Bool {
365+
// FIXME: Always return false until in memory implementation
366+
// gets symbolic link semantics.
367+
return false
368+
}
356369

357370
public func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
358371
guard let node = try getNode(path) else {
@@ -502,6 +515,10 @@ public struct RerootedFileSystemView: FileSystem {
502515
return underlyingFileSystem.isFile(formUnderlyingPath(path))
503516
}
504517

518+
public func isSymlink(_ path: AbsolutePath) -> Bool {
519+
return underlyingFileSystem.isSymlink(formUnderlyingPath(path))
520+
}
521+
505522
public func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
506523
return try underlyingFileSystem.getDirectoryContents(formUnderlyingPath(path))
507524
}

Sources/Basic/PathShims.swift

Lines changed: 21 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,13 @@ public func unlink(_ path: AbsolutePath) throws {
134134
- Warning: directories that cannot be entered due to permission problems
135135
are silently ignored. So keep that in mind.
136136

137-
- Warning: If path doesn’t exist or cannot be entered this generator will
138-
be empty. It is up to you to check `path` is valid before using this
139-
function.
140-
141137
- Warning: Symbolic links that point to directories are *not* followed.
142138

143139
- Note: setting recursively to `false` still causes the generator to feed
144140
you the directory; just not its contents.
145141
*/
146-
public func walk(_ path: AbsolutePath, recursively: Bool = true) -> RecursibleDirectoryContentsGenerator {
147-
return RecursibleDirectoryContentsGenerator(path: path, recursionFilter: { _ in recursively })
142+
public func walk(_ path: AbsolutePath, fileSystem: FileSystem = localFileSystem, recursively: Bool = true) throws -> RecursibleDirectoryContentsGenerator {
143+
return try RecursibleDirectoryContentsGenerator(path: path, fileSystem: fileSystem, recursionFilter: { _ in recursively })
148144
}
149145

150146
/**
@@ -155,86 +151,52 @@ public func walk(_ path: AbsolutePath, recursively: Bool = true) -> RecursibleDi
155151
- Warning: directories that cannot be entered due to permissions problems
156152
are silently ignored. So keep that in mind.
157153

158-
- Warning: If path doesn’t exist or cannot be entered this generator will
159-
be empty. It is up to you to check `path` is valid before using this
160-
function.
161-
162154
- Warning: Symbolic links that point to directories are *not* followed.
163155

164156
- Note: returning `false` from `recursing` still produces that directory
165157
from the generator; just not its contents.
166158
*/
167-
public func walk(_ path: AbsolutePath, recursing: (AbsolutePath) -> Bool) -> RecursibleDirectoryContentsGenerator {
168-
return RecursibleDirectoryContentsGenerator(path: path, recursionFilter: recursing)
169-
}
170-
171-
/**
172-
A generator for a single directory’s contents
173-
*/
174-
private class DirectoryContentsGenerator: IteratorProtocol {
175-
private let dirptr: DirHandle?
176-
fileprivate let path: AbsolutePath
177-
178-
fileprivate init(path: AbsolutePath) {
179-
dirptr = libc.opendir(path.asString)
180-
self.path = path
181-
}
182-
183-
deinit {
184-
if let openeddir = dirptr { closedir(openeddir) }
185-
}
186-
187-
func next() -> dirent? {
188-
guard let validdir = dirptr else { return nil } // yuck, silently ignoring the error to maintain this pattern
189-
190-
while true {
191-
var entry = dirent()
192-
var result: UnsafeMutablePointer<dirent>? = nil
193-
guard readdir_r(validdir, &entry, &result) == 0 else { continue }
194-
guard result != nil else { return nil }
195-
196-
switch (entry.d_name.0, entry.d_name.1, entry.d_name.2) {
197-
case (46, 0, _): // "."
198-
continue
199-
case (46, 46, 0): // ".."
200-
continue
201-
default:
202-
return entry
203-
}
204-
}
205-
}
159+
public func walk(_ path: AbsolutePath, fileSystem: FileSystem = localFileSystem, recursing: (AbsolutePath) -> Bool) throws -> RecursibleDirectoryContentsGenerator {
160+
return try RecursibleDirectoryContentsGenerator(path: path, fileSystem: fileSystem, recursionFilter: recursing)
206161
}
207162

208163
/**
209164
Produced by `walk`.
210165
*/
211166
public class RecursibleDirectoryContentsGenerator: IteratorProtocol, Sequence {
212-
private var current: DirectoryContentsGenerator
167+
private var current: (path: AbsolutePath, iterator: IndexingIterator<[String]>)
213168
private var towalk = [AbsolutePath]()
169+
214170
private let shouldRecurse: (AbsolutePath) -> Bool
171+
private let fileSystem: FileSystem
215172

216-
private init(path: AbsolutePath, recursionFilter: (AbsolutePath) -> Bool) {
217-
current = DirectoryContentsGenerator(path: path)
173+
private init(path: AbsolutePath, fileSystem: FileSystem, recursionFilter: (AbsolutePath) -> Bool) throws {
174+
self.fileSystem = fileSystem
175+
// FIXME: getDirectoryContents should have an iterator version.
176+
current = (path, try fileSystem.getDirectoryContents(path).makeIterator())
218177
shouldRecurse = recursionFilter
219178
}
220-
179+
221180
public func next() -> AbsolutePath? {
222181
outer: while true {
223-
guard let entry = current.next() else {
182+
guard let entry = current.iterator.next() else {
224183
while !towalk.isEmpty {
184+
// FIXME: This looks inefficient.
225185
let path = towalk.removeFirst()
226186
guard shouldRecurse(path) else { continue }
227-
current = DirectoryContentsGenerator(path: path)
187+
// Ignore if we can't get content for this path.
188+
guard let current = try? fileSystem.getDirectoryContents(path).makeIterator() else { continue }
189+
self.current = (path, current)
228190
continue outer
229191
}
230192
return nil
231193
}
232-
let name = entry.name ?? ""
233-
let path = current.path.appending(component: name)
234-
if isDirectory(path) && !isSymlink(path) {
194+
195+
let path = current.path.appending(component: entry)
196+
if fileSystem.isDirectory(path) && !fileSystem.isSymlink(path) {
235197
towalk.append(path)
236198
}
237-
return current.path.appending(component: name)
199+
return path
238200
}
239201
}
240202
}

Sources/Commands/SwiftTestTool.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public struct SwiftTestTool: SwiftTool {
187187
if maybePath.asString.exists {
188188
possibleTestPath = maybePath
189189
} else {
190-
let possiblePaths = walk(opts.path.build).filter {
190+
let possiblePaths = try walk(opts.path.build).filter {
191191
$0.basename != "Package.xctest" && // this was our hardcoded name, may still exist if no clean
192192
$0.suffix == ".xctest"
193193
}

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public struct PackageBuilder {
200200
}
201201

202202
func sourceRoot() throws -> AbsolutePath {
203-
let viableRoots = walk(packagePath, recursively: false).filter { entry in
203+
let viableRoots = try walk(packagePath, recursively: false).filter { entry in
204204
switch entry.basename.lowercased() {
205205
case "sources", "source", "src", "srcs":
206206
return entry.asString.isDirectory && !excludedPaths.contains(entry)
@@ -235,16 +235,16 @@ public struct PackageBuilder {
235235
let srcroot = try sourceRoot()
236236

237237
if srcroot != packagePath {
238-
let invalidRootFiles = walk(packagePath, recursively: false).filter(isValidSource)
238+
let invalidRootFiles = try walk(packagePath, recursively: false).filter(isValidSource)
239239
guard invalidRootFiles.isEmpty else {
240240
throw ModuleError.invalidLayout(.invalidLayout(invalidRootFiles.map{ $0.asString }))
241241
}
242242
}
243243

244-
let maybeModules = walk(srcroot, recursively: false).filter(shouldConsiderDirectory)
244+
let maybeModules = try walk(srcroot, recursively: false).filter(shouldConsiderDirectory)
245245

246246
if maybeModules.count == 1 && maybeModules[0] != srcroot {
247-
let invalidModuleFiles = walk(srcroot, recursively: false).filter(isValidSource)
247+
let invalidModuleFiles = try walk(srcroot, recursively: false).filter(isValidSource)
248248
guard invalidModuleFiles.isEmpty else {
249249
throw ModuleError.invalidLayout(.invalidLayout(invalidModuleFiles.map{ $0.asString }))
250250
}
@@ -313,7 +313,7 @@ public struct PackageBuilder {
313313
}
314314

315315
private func modulify(_ path: AbsolutePath, name: String, isTest: Bool) throws -> Module {
316-
let walked = walk(path, recursing: shouldConsiderDirectory).map{ $0 }
316+
let walked = try walk(path, recursing: shouldConsiderDirectory).map{ $0 }
317317

318318
let cSources = walked.filter{ isValidSource($0, validExtensions: SupportedLanguageExtension.cFamilyExtensions) }
319319
let swiftSources = walked.filter{ isValidSource($0, validExtensions: SupportedLanguageExtension.swiftExtensions) }
@@ -401,7 +401,7 @@ public struct PackageBuilder {
401401
let testsPath = packagePath.appending("Tests")
402402

403403
// Don't try to walk Tests if it is in excludes.
404-
if testsPath.asString.isDirectory && excludedPaths.contains(testsPath) { return [] }
404+
guard testsPath.asString.isDirectory && !excludedPaths.contains(testsPath) else { return [] }
405405

406406
// Create the test modules
407407
let testModules = try walk(testsPath, recursively: false).filter(shouldConsiderDirectory).flatMap { dir in

Sources/Xcodeproj/generate().swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,16 @@ func open(_ path: AbsolutePath, body: ((String) -> Void) throws -> Void) throws
138138
/// Finds directories that will be added as blue folder
139139
/// Excludes hidden directories and Xcode projects and directories that contains source code
140140
func findDirectoryReferences(path: AbsolutePath) throws -> [AbsolutePath] {
141-
let rootDirectories = walk(path, recursively: false)
141+
let rootDirectories = try walk(path, recursively: false)
142142
let rootDirectoriesToConsider = rootDirectories.filter {
143143
if $0.suffix == ".xcodeproj" { return false }
144144
if $0.suffix == ".playground" { return false }
145145
if $0.basename.hasPrefix(".") { return false }
146146
return $0.asString.isDirectory
147147
}
148148

149-
let filteredDirectories = rootDirectoriesToConsider.filter {
150-
let directoriesWithSources = walk($0).filter {
149+
let filteredDirectories = try rootDirectoriesToConsider.filter {
150+
let directoriesWithSources = try walk($0).filter {
151151
guard let fileExt = $0.asString.fileExt else { return false }
152152
return SupportedLanguageExtension.validExtensions.contains(fileExt)
153153
}

Tests/Basic/FileSystemTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ class FileSystemTests: XCTestCase {
4242
XCTAssertTrue(fs.isFile(file.path))
4343
XCTAssertFalse(fs.isDirectory(file.path))
4444
XCTAssertFalse(fs.isFile("/does-not-exist"))
45+
XCTAssertFalse(fs.isSymlink("/does-not-exist"))
46+
47+
// isSymlink()
48+
let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true)
49+
let sym = tempDir.path.appending("hello")
50+
try! symlink(sym, pointingAt: file.path)
51+
XCTAssertTrue(fs.isSymlink(sym))
52+
XCTAssertTrue(fs.isFile(sym))
53+
XCTAssertFalse(fs.isDirectory(sym))
4554

4655
// isDirectory()
4756
XCTAssert(fs.isDirectory("/"))
@@ -146,6 +155,9 @@ class FileSystemTests: XCTestCase {
146155
// isFile()
147156
XCTAssert(!fs.isFile("/does-not-exist"))
148157

158+
// isSymlink()
159+
XCTAssert(!fs.isSymlink("/does-not-exist"))
160+
149161
// getDirectoryContents()
150162
XCTAssertThrows(FileSystemError.noEntry) {
151163
_ = try fs.getDirectoryContents("/does-not-exist")
@@ -206,6 +218,7 @@ class FileSystemTests: XCTestCase {
206218
try! fs.writeFileContents(filePath, bytes: "Hello, world!")
207219
XCTAssert(fs.exists(filePath))
208220
XCTAssertTrue(fs.isFile(filePath))
221+
XCTAssertFalse(fs.isSymlink(filePath))
209222
XCTAssert(!fs.isDirectory(filePath))
210223
XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, world!")
211224

Tests/Basic/PathShimTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class WalkTests : XCTestCase {
102102
AbsolutePath("/bin"),
103103
AbsolutePath("/sbin")
104104
]
105-
for x in walk("/", recursively: false) {
105+
for x in try! walk("/", recursively: false) {
106106
if let i = expected.index(of: x) {
107107
expected.remove(at: i)
108108
}
@@ -117,7 +117,7 @@ class WalkTests : XCTestCase {
117117
root.appending(component: "Build"),
118118
root.appending(component: "Utility")
119119
]
120-
for x in walk(root) {
120+
for x in try! walk(root) {
121121
if let i = expected.index(of: x) {
122122
expected.remove(at: i)
123123
}
@@ -138,7 +138,7 @@ class WalkTests : XCTestCase {
138138
XCTAssertEqual(resolveSymlinks(tmpDirPath.appending("foo/symlink")), tmpDirPath.appending("bar"))
139139
XCTAssertTrue(resolveSymlinks(tmpDirPath.appending("foo/symlink/baz")).asString.isDirectory)
140140

141-
let results = walk(tmpDirPath.appending("foo")).map{ $0 }
141+
let results = try! walk(tmpDirPath.appending("foo")).map{ $0 }
142142

143143
XCTAssertEqual(results, [tmpDirPath.appending("foo/symlink")])
144144
}
@@ -154,7 +154,7 @@ class WalkTests : XCTestCase {
154154

155155
XCTAssertTrue(tmpDirPath.appending("symlink").asString.isSymlink)
156156

157-
let results = walk(tmpDirPath.appending("symlink")).map{ $0 }.sorted()
157+
let results = try! walk(tmpDirPath.appending("symlink")).map{ $0 }.sorted()
158158

159159
// we recurse a symlink to a directory, so this should work,
160160
// but `abc` should not show because `baz` is a symlink too

0 commit comments

Comments
 (0)