Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions Sources/Basic/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public enum FileMode {
// FIXME: Design an asynchronous story?
public protocol FileSystem: class {
/// Check whether the given path exists and is accessible.
func exists(_ path: AbsolutePath) -> Bool
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool

/// Check whether the given path is accessible and a directory.
func isDirectory(_ path: AbsolutePath) -> Bool
Expand Down Expand Up @@ -168,6 +168,11 @@ public protocol FileSystem: class {
/// Convenience implementations (default arguments aren't permitted in protocol
/// methods).
public extension FileSystem {
/// exists override with default value.
func exists(_ path: AbsolutePath) -> Bool {
return exists(path, followSymlink: true)
}

/// Default implementation of createDirectory(_:)
func createDirectory(_ path: AbsolutePath) throws {
try createDirectory(path, recursive: false)
Expand Down Expand Up @@ -197,8 +202,8 @@ private class LocalFileSystem: FileSystem {
return filestat.st_mode & libc.S_IXUSR != 0
}

func exists(_ path: AbsolutePath) -> Bool {
return Basic.exists(path)
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
return Basic.exists(path, followSymlink: followSymlink)
}

func isDirectory(_ path: AbsolutePath) -> Bool {
Expand Down Expand Up @@ -332,7 +337,7 @@ private class LocalFileSystem: FileSystem {
}

func removeFileTree(_ path: AbsolutePath) throws {
if self.exists(path) {
if self.exists(path, followSymlink: false) {
try Basic.removeFileTree(path)
}
}
Expand Down Expand Up @@ -509,7 +514,7 @@ public class InMemoryFileSystem: FileSystem {

// MARK: FileSystem Implementation

public func exists(_ path: AbsolutePath) -> Bool {
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
do {
return try getNode(path) != nil
} catch {
Expand Down Expand Up @@ -706,8 +711,8 @@ public class RerootedFileSystemView: FileSystem {

// MARK: FileSystem Implementation

public func exists(_ path: AbsolutePath) -> Bool {
return underlyingFileSystem.exists(formUnderlyingPath(path))
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
return underlyingFileSystem.exists(formUnderlyingPath(path), followSymlink: followSymlink)
}

public func isDirectory(_ path: AbsolutePath) -> Bool {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Basic/PathShims.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public func makeDirectories(_ path: AbsolutePath) throws {
}

/// Recursively deletes the file system entity at `path`. If there is no file system entity at `path`, this function
/// does nothing (in particular, this is not considered to be an error).
/// throws an error.
public func removeFileTree(_ path: AbsolutePath) throws {
try FileManager.default.removeItem(atPath: path.asString)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SourceControl/GitRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ private class GitFileSystemView: FileSystem {
return tree
}

func exists(_ path: AbsolutePath) -> Bool {
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
do {
return try getEntry(path) != nil
} catch {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SourceControl/InMemoryGitRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ public final class InMemoryGitRepository {

extension InMemoryGitRepository: FileSystem {

public func exists(_ path: AbsolutePath) -> Bool {
return head.fileSystem.exists(path)
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
return head.fileSystem.exists(path, followSymlink: followSymlink)
}

public func isDirectory(_ path: AbsolutePath) -> Bool {
Expand Down
32 changes: 32 additions & 0 deletions Tests/BasicTests/FileSystemTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,37 @@ class FileSystemTests: XCTestCase {
XCTAssertTrue(thisDirectoryContents.contains(where: { $0 == AbsolutePath(#file).basename }))
}

func testLocalExistsSymlink() throws {
mktmpdir { path in
let fs = Basic.localFileSystem

let source = path.appending(component: "source")
let target = path.appending(component: "target")
try fs.writeFileContents(target, bytes: "source")

// Source and target exist.

try createSymlink(source, pointingAt: target)
XCTAssertEqual(fs.exists(source), true)
XCTAssertEqual(fs.exists(source, followSymlink: true), true)
XCTAssertEqual(fs.exists(source, followSymlink: false), true)

// Source only exists.

try fs.removeFileTree(target)
XCTAssertEqual(fs.exists(source), false)
XCTAssertEqual(fs.exists(source, followSymlink: true), false)
XCTAssertEqual(fs.exists(source, followSymlink: false), true)

// None exist.

try fs.removeFileTree(source)
XCTAssertEqual(fs.exists(source), false)
XCTAssertEqual(fs.exists(source, followSymlink: true), false)
XCTAssertEqual(fs.exists(source, followSymlink: false), false)
}
}

func testLocalCreateDirectory() throws {
let fs = Basic.localFileSystem

Expand Down Expand Up @@ -378,6 +409,7 @@ class FileSystemTests: XCTestCase {
("testLocalBasics", testLocalBasics),
("testLocalCreateDirectory", testLocalCreateDirectory),
("testLocalReadWriteFile", testLocalReadWriteFile),
("testLocalExistsSymlink", testLocalExistsSymlink),
("testInMemoryBasics", testInMemoryBasics),
("testInMemoryCreateDirectory", testInMemoryCreateDirectory),
("testInMemoryFsCopy", testInMemoryFsCopy),
Expand Down